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 }