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 }