github.com/ethersphere/bee/v2@v2.2.0/pkg/accesscontrol/access.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  	"errors"
    11  	"fmt"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/accesscontrol/kvs"
    14  	"github.com/ethersphere/bee/v2/pkg/encryption"
    15  	"github.com/ethersphere/bee/v2/pkg/swarm"
    16  	"golang.org/x/crypto/sha3"
    17  )
    18  
    19  //nolint:gochecknoglobals
    20  var (
    21  	hashFunc      = sha3.NewLegacyKeccak256
    22  	oneByteArray  = []byte{1}
    23  	zeroByteArray = []byte{0}
    24  )
    25  
    26  // Decryptor is a read-only interface for the ACT.
    27  type Decryptor interface {
    28  	// DecryptRef will return a decrypted reference, for given encrypted reference and grantee.
    29  	DecryptRef(ctx context.Context, storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error)
    30  	Session
    31  }
    32  
    33  // Control interface for the ACT (does write operations).
    34  type Control interface {
    35  	Decryptor
    36  	// AddGrantee adds a new grantee to the ACT.
    37  	AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey) error
    38  	// EncryptRef encrypts a Swarm reference for a given grantee.
    39  	EncryptRef(ctx context.Context, storage kvs.KeyValueStore, grantee *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error)
    40  }
    41  
    42  // ActLogic represents the access control logic.
    43  type ActLogic struct {
    44  	Session
    45  }
    46  
    47  var _ Control = (*ActLogic)(nil)
    48  
    49  // EncryptRef encrypts a Swarm reference for a publisher.
    50  func (al ActLogic) EncryptRef(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey, ref swarm.Address) (swarm.Address, error) {
    51  	accessKey, err := al.getAccessKey(ctx, storage, publisherPubKey)
    52  	if err != nil {
    53  		return swarm.ZeroAddress, err
    54  	}
    55  	refCipher := encryption.New(accessKey, 0, 0, hashFunc)
    56  	encryptedRef, err := refCipher.Encrypt(ref.Bytes())
    57  	if err != nil {
    58  		return swarm.ZeroAddress, fmt.Errorf("failed to encrypt reference: %w", err)
    59  	}
    60  
    61  	return swarm.NewAddress(encryptedRef), nil
    62  }
    63  
    64  // AddGrantee adds a new grantee to the ACT.
    65  func (al ActLogic) AddGrantee(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey, granteePubKey *ecdsa.PublicKey) error {
    66  	var (
    67  		accessKey encryption.Key
    68  		err       error
    69  	)
    70  
    71  	// Create new access key because grantee is the publisher.
    72  	if publisherPubKey.Equal(granteePubKey) {
    73  		accessKey = encryption.GenerateRandomKey(encryption.KeyLength)
    74  	} else {
    75  		// Get previously generated access key.
    76  		accessKey, err = al.getAccessKey(ctx, storage, publisherPubKey)
    77  		if err != nil {
    78  			return err
    79  		}
    80  	}
    81  
    82  	lookupKey, accessKeyDecryptionKey, err := al.getKeys(granteePubKey)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	// Encrypt the access key for the new Grantee.
    88  	cipher := encryption.New(encryption.Key(accessKeyDecryptionKey), 0, 0, hashFunc)
    89  	granteeEncryptedAccessKey, err := cipher.Encrypt(accessKey)
    90  	if err != nil {
    91  		return fmt.Errorf("failed to encrypt access key: %w", err)
    92  	}
    93  
    94  	// Add the new encrypted access key to the Act.
    95  	err = storage.Put(ctx, lookupKey, granteeEncryptedAccessKey)
    96  	if err != nil {
    97  		return fmt.Errorf("failed to put value to KVS: %w", err)
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  // Will return the access key for a publisher (public key).
   104  func (al *ActLogic) getAccessKey(ctx context.Context, storage kvs.KeyValueStore, publisherPubKey *ecdsa.PublicKey) ([]byte, error) {
   105  	publisherLookupKey, publisherAKDecryptionKey, err := al.getKeys(publisherPubKey)
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	// no need for constructor call if value not found in act.
   110  	accessKeyDecryptionCipher := encryption.New(encryption.Key(publisherAKDecryptionKey), 0, 0, hashFunc)
   111  	encryptedAK, err := storage.Get(ctx, publisherLookupKey)
   112  	if err != nil {
   113  		switch {
   114  		case errors.Is(err, kvs.ErrNotFound):
   115  			return nil, ErrNotFound
   116  		default:
   117  			return nil, fmt.Errorf("failed go get value from KVS: %w", err)
   118  		}
   119  	}
   120  
   121  	accessKey, err := accessKeyDecryptionCipher.Decrypt(encryptedAK)
   122  	if err != nil {
   123  		return nil, fmt.Errorf("failed to decrypt access key: %w", err)
   124  	}
   125  
   126  	return accessKey, nil
   127  }
   128  
   129  // Generate lookup key and access key decryption key for a given public key.
   130  func (al *ActLogic) getKeys(publicKey *ecdsa.PublicKey) ([]byte, []byte, error) {
   131  	nonces := [][]byte{zeroByteArray, oneByteArray}
   132  	keys, err := al.Session.Key(publicKey, nonces)
   133  	if len(keys) != len(nonces) {
   134  		return nil, nil, err
   135  	}
   136  	return keys[0], keys[1], err
   137  }
   138  
   139  // DecryptRef will return a decrypted reference, for given encrypted reference and publisher.
   140  func (al ActLogic) DecryptRef(ctx context.Context, storage kvs.KeyValueStore, encryptedRef swarm.Address, publisher *ecdsa.PublicKey) (swarm.Address, error) {
   141  	accessKey, err := al.getAccessKey(ctx, storage, publisher)
   142  	if err != nil {
   143  		return swarm.ZeroAddress, err
   144  	}
   145  
   146  	refCipher := encryption.New(accessKey, 0, 0, hashFunc)
   147  	ref, err := refCipher.Decrypt(encryptedRef.Bytes())
   148  	if err != nil {
   149  		return swarm.ZeroAddress, fmt.Errorf("failed to decrypt reference: %w", err)
   150  	}
   151  
   152  	return swarm.NewAddress(ref), nil
   153  }
   154  
   155  // NewLogic creates a new ACT Logic from a session.
   156  func NewLogic(s Session) ActLogic {
   157  	return ActLogic{
   158  		Session: s,
   159  	}
   160  }