github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/libkbfs/block_ops_test.go (about) 1 // Copyright 2016 Keybase Inc. All rights reserved. 2 // Use of this source code is governed by a BSD 3 // license that can be found in the LICENSE file. 4 5 package libkbfs 6 7 import ( 8 "fmt" 9 "sync" 10 "testing" 11 12 "github.com/golang/mock/gomock" 13 "github.com/keybase/client/go/kbfs/data" 14 "github.com/keybase/client/go/kbfs/env" 15 "github.com/keybase/client/go/kbfs/kbfsblock" 16 "github.com/keybase/client/go/kbfs/kbfscodec" 17 "github.com/keybase/client/go/kbfs/kbfscrypto" 18 "github.com/keybase/client/go/kbfs/kbfshash" 19 "github.com/keybase/client/go/kbfs/kbfsmd" 20 "github.com/keybase/client/go/kbfs/libkey" 21 "github.com/keybase/client/go/kbfs/test/clocktest" 22 "github.com/keybase/client/go/kbfs/tlf" 23 "github.com/keybase/client/go/protocol/keybase1" 24 "github.com/pkg/errors" 25 "github.com/stretchr/testify/require" 26 "golang.org/x/net/context" 27 ) 28 29 // fakeKeyMetadata is an implementation of KeyMetadata that just 30 // stores TLFCryptKeys directly. It's meant to be used with 31 // fakeBlockKeyGetter. 32 type fakeKeyMetadata struct { 33 // Embed a KeyMetadata that's always empty, so that all 34 // methods besides TlfID() panic. 35 libkey.KeyMetadata 36 tlfID tlf.ID 37 keys []kbfscrypto.TLFCryptKey 38 } 39 40 var _ libkey.KeyMetadata = fakeKeyMetadata{} 41 42 // makeFakeKeyMetadata returns a fakeKeyMetadata with keys for each 43 // KeyGen up to latestKeyGen. The key for KeyGen i is a deterministic 44 // function of i, so multiple calls to this function will have the 45 // same keys. 46 func makeFakeKeyMetadata(tlfID tlf.ID, latestKeyGen kbfsmd.KeyGen) fakeKeyMetadata { 47 keys := make([]kbfscrypto.TLFCryptKey, 0, 48 latestKeyGen-kbfsmd.FirstValidKeyGen+1) 49 for keyGen := kbfsmd.FirstValidKeyGen; keyGen <= latestKeyGen; keyGen++ { 50 keys = append(keys, 51 kbfscrypto.MakeTLFCryptKey([32]byte{byte(keyGen)})) 52 } 53 return fakeKeyMetadata{nil, tlfID, keys} 54 } 55 56 func (kmd fakeKeyMetadata) TlfID() tlf.ID { 57 return kmd.tlfID 58 } 59 60 type fakeBlockKeyGetter struct{} 61 62 func (kg fakeBlockKeyGetter) GetTLFCryptKeyForEncryption( 63 ctx context.Context, kmd libkey.KeyMetadata) (kbfscrypto.TLFCryptKey, error) { 64 fkmd := kmd.(fakeKeyMetadata) 65 if len(fkmd.keys) == 0 { 66 return kbfscrypto.TLFCryptKey{}, errors.New( 67 "no keys for encryption") 68 } 69 return fkmd.keys[len(fkmd.keys)-1], nil 70 } 71 72 func (kg fakeBlockKeyGetter) GetTLFCryptKeyForBlockDecryption( 73 ctx context.Context, kmd libkey.KeyMetadata, blockPtr data.BlockPointer) ( 74 kbfscrypto.TLFCryptKey, error) { 75 fkmd := kmd.(fakeKeyMetadata) 76 i := int(blockPtr.KeyGen - kbfsmd.FirstValidKeyGen) 77 if i >= len(fkmd.keys) { 78 return kbfscrypto.TLFCryptKey{}, errors.Errorf( 79 "no key for block decryption (keygen=%d)", 80 blockPtr.KeyGen) 81 } 82 return fkmd.keys[i], nil 83 } 84 85 type testBlockOpsConfig struct { 86 testCodecGetter 87 logMaker 88 bserver BlockServer 89 cp cryptoPure 90 cache data.BlockCache 91 diskBlockCacheGetter 92 *testSyncedTlfGetterSetter 93 initModeGetter 94 clock Clock 95 reporter Reporter 96 subscriptionManager SubscriptionManager 97 subscriptionManagerPublisher SubscriptionManagerPublisher 98 } 99 100 var _ blockOpsConfig = (*testBlockOpsConfig)(nil) 101 102 func (config testBlockOpsConfig) BlockServer() BlockServer { 103 return config.bserver 104 } 105 106 func (config testBlockOpsConfig) cryptoPure() cryptoPure { 107 return config.cp 108 } 109 110 func (config testBlockOpsConfig) keyGetter() blockKeyGetter { 111 return fakeBlockKeyGetter{} 112 } 113 114 func (config testBlockOpsConfig) BlockCache() data.BlockCache { 115 return config.cache 116 } 117 118 func (config testBlockOpsConfig) DataVersion() data.Ver { 119 return data.ChildHolesVer 120 } 121 122 func (config testBlockOpsConfig) BlockCryptVersion() kbfscrypto.EncryptionVer { 123 return kbfscrypto.EncryptionSecretboxWithKeyNonce 124 } 125 126 func (config testBlockOpsConfig) Clock() Clock { 127 return config.clock 128 } 129 130 func (config testBlockOpsConfig) Reporter() Reporter { 131 return config.reporter 132 } 133 134 func (config testBlockOpsConfig) GetSettingsDB() *SettingsDB { 135 return nil 136 } 137 138 func (config testBlockOpsConfig) SubscriptionManager( 139 _ SubscriptionManagerClientID, _ bool, 140 _ SubscriptionNotifier) SubscriptionManager { 141 return config.subscriptionManager 142 } 143 144 func (config testBlockOpsConfig) SubscriptionManagerPublisher() SubscriptionManagerPublisher { 145 return config.subscriptionManagerPublisher 146 } 147 148 func makeTestBlockOpsConfig(t *testing.T) testBlockOpsConfig { 149 lm := newTestLogMaker(t) 150 codecGetter := newTestCodecGetter() 151 bserver := NewBlockServerMemory(lm.MakeLogger("")) 152 crypto := MakeCryptoCommon(codecGetter.Codec(), makeBlockCryptV1()) 153 cache := data.NewBlockCacheStandard(10, getDefaultCleanBlockCacheCapacity(NewInitModeFromType(InitDefault))) 154 dbcg := newTestDiskBlockCacheGetter(t, nil) 155 stgs := newTestSyncedTlfGetterSetter() 156 clock := clocktest.NewTestClockNow() 157 mockPublisher := NewMockSubscriptionManagerPublisher(gomock.NewController(t)) 158 mockPublisher.EXPECT().PublishChange(gomock.Any()).AnyTimes() 159 return testBlockOpsConfig{codecGetter, lm, bserver, crypto, cache, dbcg, 160 stgs, testInitModeGetter{InitDefault}, clock, 161 NewReporterSimple(clock, 1), nil, mockPublisher} 162 } 163 164 func testBlockOpsShutdown( 165 ctx context.Context, t *testing.T, bops *BlockOpsStandard) { 166 err := bops.Shutdown(ctx) 167 require.NoError(t, err) 168 } 169 170 // TestBlockOpsReadySuccess checks that BlockOpsStandard.Ready() 171 // encrypts its given block properly. 172 func TestBlockOpsReadySuccess(t *testing.T) { 173 ctx := context.Background() 174 config := makeTestBlockOpsConfig(t) 175 bops := NewBlockOpsStandard( 176 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 177 0, env.EmptyAppStateUpdater{}) 178 defer testBlockOpsShutdown(ctx, t, bops) 179 180 tlfID := tlf.FakeID(0, tlf.Private) 181 var latestKeyGen kbfsmd.KeyGen = 5 182 kmd := makeFakeKeyMetadata(tlfID, latestKeyGen) 183 184 block := &data.FileBlock{ 185 Contents: []byte{1, 2, 3, 4, 5}, 186 } 187 188 encodedBlock, err := config.Codec().Encode(block) 189 require.NoError(t, err) 190 191 id, plainSize, readyBlockData, err := bops.Ready(ctx, kmd, block) 192 require.NoError(t, err) 193 194 require.Equal(t, len(encodedBlock), plainSize) 195 196 err = kbfsblock.VerifyID(readyBlockData.Buf, id) 197 require.NoError(t, err) 198 199 var encryptedBlock kbfscrypto.EncryptedBlock 200 err = config.Codec().Decode(readyBlockData.Buf, &encryptedBlock) 201 require.NoError(t, err) 202 203 decryptedBlock := &data.FileBlock{} 204 err = config.cryptoPure().DecryptBlock( 205 encryptedBlock, kmd.keys[latestKeyGen-kbfsmd.FirstValidKeyGen], 206 readyBlockData.ServerHalf, decryptedBlock) 207 require.NoError(t, err) 208 decryptedBlock.SetEncodedSize(uint32(readyBlockData.GetEncodedSize())) 209 require.Equal(t, block, decryptedBlock) 210 } 211 212 // TestBlockOpsReadyFailKeyGet checks that BlockOpsStandard.Ready() 213 // fails properly if we fail to retrieve the key. 214 func TestBlockOpsReadyFailKeyGet(t *testing.T) { 215 ctx := context.Background() 216 config := makeTestBlockOpsConfig(t) 217 bops := NewBlockOpsStandard( 218 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 219 0, env.EmptyAppStateUpdater{}) 220 defer testBlockOpsShutdown(ctx, t, bops) 221 222 tlfID := tlf.FakeID(0, tlf.Private) 223 kmd := makeFakeKeyMetadata(tlfID, 0) 224 225 _, _, _, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 226 require.EqualError(t, err, "no keys for encryption") 227 } 228 229 type badServerHalfMaker struct { 230 cryptoPure 231 } 232 233 func (c badServerHalfMaker) MakeRandomBlockCryptKeyServerHalf() ( 234 kbfscrypto.BlockCryptKeyServerHalf, error) { 235 return kbfscrypto.BlockCryptKeyServerHalf{}, errors.New( 236 "could not make server half") 237 } 238 239 // TestBlockOpsReadyFailServerHalfGet checks that BlockOpsStandard.Ready() 240 // fails properly if we fail to generate a server half. 241 func TestBlockOpsReadyFailServerHalfGet(t *testing.T) { 242 ctx := context.Background() 243 config := makeTestBlockOpsConfig(t) 244 config.cp = badServerHalfMaker{config.cryptoPure()} 245 bops := NewBlockOpsStandard( 246 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 247 0, env.EmptyAppStateUpdater{}) 248 defer testBlockOpsShutdown(ctx, t, bops) 249 250 tlfID := tlf.FakeID(0, tlf.Private) 251 kmd := makeFakeKeyMetadata(tlfID, kbfsmd.FirstValidKeyGen) 252 253 _, _, _, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 254 require.EqualError(t, err, "could not make server half") 255 } 256 257 type badBlockEncryptor struct { 258 cryptoPure 259 } 260 261 func (c badBlockEncryptor) EncryptBlock( 262 block data.Block, tlfCryptKey kbfscrypto.TLFCryptKey, 263 blockServerHalf kbfscrypto.BlockCryptKeyServerHalf) ( 264 plainSize int, encryptedBlock kbfscrypto.EncryptedBlock, err error) { 265 return 0, kbfscrypto.EncryptedBlock{}, errors.New("could not encrypt block") 266 } 267 268 // TestBlockOpsReadyFailEncryption checks that BlockOpsStandard.Ready() 269 // fails properly if we fail to encrypt the block. 270 func TestBlockOpsReadyFailEncryption(t *testing.T) { 271 ctx := context.Background() 272 config := makeTestBlockOpsConfig(t) 273 config.cp = badBlockEncryptor{config.cryptoPure()} 274 bops := NewBlockOpsStandard( 275 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 276 0, env.EmptyAppStateUpdater{}) 277 defer testBlockOpsShutdown(ctx, t, bops) 278 279 tlfID := tlf.FakeID(0, tlf.Private) 280 kmd := makeFakeKeyMetadata(tlfID, kbfsmd.FirstValidKeyGen) 281 282 _, _, _, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 283 require.EqualError(t, err, "could not encrypt block") 284 } 285 286 type badEncoder struct { 287 kbfscodec.Codec 288 } 289 290 func (c badEncoder) Encode(o interface{}) ([]byte, error) { 291 return nil, errors.New("could not encode") 292 } 293 294 // TestBlockOpsReadyFailEncode checks that BlockOpsStandard.Ready() 295 // fails properly if we fail to encode the encrypted block. 296 func TestBlockOpsReadyFailEncode(t *testing.T) { 297 ctx := context.Background() 298 config := makeTestBlockOpsConfig(t) 299 config.testCodecGetter.codec = badEncoder{config.codec} 300 bops := NewBlockOpsStandard( 301 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 302 0, env.EmptyAppStateUpdater{}) 303 defer testBlockOpsShutdown(ctx, t, bops) 304 305 tlfID := tlf.FakeID(0, tlf.Private) 306 kmd := makeFakeKeyMetadata(tlfID, kbfsmd.FirstValidKeyGen) 307 308 _, _, _, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 309 require.EqualError(t, err, "could not encode") 310 } 311 312 type tooSmallEncoder struct { 313 kbfscodec.Codec 314 } 315 316 func (c tooSmallEncoder) Encode(o interface{}) ([]byte, error) { 317 return []byte{0x1}, nil 318 } 319 320 // TestBlockOpsReadyTooSmallEncode checks that 321 // BlockOpsStandard.Ready() fails properly if the encrypted block 322 // encodes to a too-small buffer. 323 func TestBlockOpsReadyTooSmallEncode(t *testing.T) { 324 ctx := context.Background() 325 config := makeTestBlockOpsConfig(t) 326 config.codec = tooSmallEncoder{config.codec} 327 bops := NewBlockOpsStandard( 328 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 329 0, env.EmptyAppStateUpdater{}) 330 defer testBlockOpsShutdown(ctx, t, bops) 331 332 tlfID := tlf.FakeID(0, tlf.Private) 333 kmd := makeFakeKeyMetadata(tlfID, kbfsmd.FirstValidKeyGen) 334 335 _, _, _, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 336 require.IsType(t, TooLowByteCountError{}, err) 337 } 338 339 // TestBlockOpsReadySuccess checks that BlockOpsStandard.Get() 340 // retrieves a block properly, even if that block was encoded for a 341 // previous key generation. 342 func TestBlockOpsGetSuccess(t *testing.T) { 343 ctx := context.Background() 344 config := makeTestBlockOpsConfig(t) 345 bops := NewBlockOpsStandard( 346 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 347 0, env.EmptyAppStateUpdater{}) 348 defer testBlockOpsShutdown(ctx, t, bops) 349 350 tlfID := tlf.FakeID(0, tlf.Private) 351 var keyGen kbfsmd.KeyGen = 3 352 kmd1 := makeFakeKeyMetadata(tlfID, keyGen) 353 354 block := &data.FileBlock{ 355 Contents: []byte{1, 2, 3, 4, 5}, 356 } 357 358 id, _, readyBlockData, err := bops.Ready(ctx, kmd1, block) 359 require.NoError(t, err) 360 361 bCtx := kbfsblock.MakeFirstContext( 362 keybase1.MakeTestUID(1).AsUserOrTeam(), keybase1.BlockType_DATA) 363 err = config.bserver.Put( 364 ctx, tlfID, id, bCtx, readyBlockData.Buf, readyBlockData.ServerHalf, 365 DiskBlockAnyCache) 366 require.NoError(t, err) 367 368 kmd2 := makeFakeKeyMetadata(tlfID, keyGen+3) 369 decryptedBlock := &data.FileBlock{} 370 err = bops.Get(ctx, kmd2, 371 data.BlockPointer{ID: id, DataVer: data.FirstValidVer, 372 KeyGen: keyGen, Context: bCtx}, 373 decryptedBlock, data.NoCacheEntry, data.MasterBranch) 374 require.NoError(t, err) 375 require.Equal(t, block, decryptedBlock) 376 } 377 378 // TestBlockOpsReadySuccess checks that BlockOpsStandard.Get() fails 379 // if it can't retrieve the block from the server. 380 func TestBlockOpsGetFailServerGet(t *testing.T) { 381 ctx := context.Background() 382 config := makeTestBlockOpsConfig(t) 383 bops := NewBlockOpsStandard( 384 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 385 0, env.EmptyAppStateUpdater{}) 386 defer testBlockOpsShutdown(ctx, t, bops) 387 388 tlfID := tlf.FakeID(0, tlf.Private) 389 var latestKeyGen kbfsmd.KeyGen = 5 390 kmd := makeFakeKeyMetadata(tlfID, latestKeyGen) 391 392 id, _, _, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 393 require.NoError(t, err) 394 395 bCtx := kbfsblock.MakeFirstContext( 396 keybase1.MakeTestUID(1).AsUserOrTeam(), keybase1.BlockType_DATA) 397 var decryptedBlock data.FileBlock 398 err = bops.Get(ctx, kmd, 399 data.BlockPointer{ID: id, DataVer: data.FirstValidVer, 400 KeyGen: latestKeyGen, Context: bCtx}, 401 &decryptedBlock, data.NoCacheEntry, data.MasterBranch) 402 require.IsType(t, kbfsblock.ServerErrorBlockNonExistent{}, err) 403 } 404 405 type badGetBlockServer struct { 406 BlockServer 407 } 408 409 func (bserver badGetBlockServer) Get( 410 ctx context.Context, tlfID tlf.ID, id kbfsblock.ID, 411 context kbfsblock.Context, cacheType DiskBlockCacheType) ( 412 []byte, kbfscrypto.BlockCryptKeyServerHalf, error) { 413 buf, serverHalf, err := bserver.BlockServer.Get( 414 ctx, tlfID, id, context, cacheType) 415 if err != nil { 416 return nil, kbfscrypto.BlockCryptKeyServerHalf{}, nil 417 } 418 419 return append(buf, 0x1), serverHalf, nil 420 } 421 422 // TestBlockOpsReadyFailVerify checks that BlockOpsStandard.Get() 423 // fails if it can't verify the block retrieved from the server. 424 func TestBlockOpsGetFailVerify(t *testing.T) { 425 ctx := context.Background() 426 config := makeTestBlockOpsConfig(t) 427 config.bserver = badGetBlockServer{config.bserver} 428 bops := NewBlockOpsStandard( 429 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 430 0, env.EmptyAppStateUpdater{}) 431 defer testBlockOpsShutdown(ctx, t, bops) 432 433 tlfID := tlf.FakeID(0, tlf.Private) 434 var latestKeyGen kbfsmd.KeyGen = 5 435 kmd := makeFakeKeyMetadata(tlfID, latestKeyGen) 436 437 id, _, readyBlockData, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 438 require.NoError(t, err) 439 440 bCtx := kbfsblock.MakeFirstContext( 441 keybase1.MakeTestUID(1).AsUserOrTeam(), keybase1.BlockType_DATA) 442 err = config.bserver.Put( 443 ctx, tlfID, id, bCtx, readyBlockData.Buf, readyBlockData.ServerHalf, 444 DiskBlockAnyCache) 445 require.NoError(t, err) 446 447 var decryptedBlock data.FileBlock 448 err = bops.Get(ctx, kmd, 449 data.BlockPointer{ID: id, DataVer: data.FirstValidVer, 450 KeyGen: latestKeyGen, Context: bCtx}, 451 &decryptedBlock, data.NoCacheEntry, data.MasterBranch) 452 require.IsType(t, kbfshash.HashMismatchError{}, errors.Cause(err)) 453 } 454 455 // TestBlockOpsReadyFailKeyGet checks that BlockOpsStandard.Get() 456 // fails if it can't get the decryption key. 457 func TestBlockOpsGetFailKeyGet(t *testing.T) { 458 ctx := context.Background() 459 config := makeTestBlockOpsConfig(t) 460 bops := NewBlockOpsStandard( 461 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 462 0, env.EmptyAppStateUpdater{}) 463 defer testBlockOpsShutdown(ctx, t, bops) 464 465 tlfID := tlf.FakeID(0, tlf.Private) 466 var latestKeyGen kbfsmd.KeyGen = 5 467 kmd := makeFakeKeyMetadata(tlfID, latestKeyGen) 468 469 id, _, readyBlockData, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 470 require.NoError(t, err) 471 472 bCtx := kbfsblock.MakeFirstContext( 473 keybase1.MakeTestUID(1).AsUserOrTeam(), keybase1.BlockType_DATA) 474 err = config.bserver.Put( 475 ctx, tlfID, id, bCtx, readyBlockData.Buf, readyBlockData.ServerHalf, 476 DiskBlockAnyCache) 477 require.NoError(t, err) 478 479 var decryptedBlock data.FileBlock 480 err = bops.Get(ctx, kmd, 481 data.BlockPointer{ID: id, DataVer: data.FirstValidVer, 482 KeyGen: latestKeyGen + 1, Context: bCtx}, 483 &decryptedBlock, data.NoCacheEntry, data.MasterBranch) 484 require.EqualError(t, err, fmt.Sprintf( 485 "no key for block decryption (keygen=%d)", latestKeyGen+1)) 486 } 487 488 // badDecoder maintains a map from stringified byte buffers to 489 // error. If Decode is called with a buffer that matches anything in 490 // the map, the corresponding error is returned. 491 // 492 // This is necessary because codec functions are used everywhere. 493 type badDecoder struct { 494 kbfscodec.Codec 495 496 errorsLock sync.RWMutex 497 errors map[string]error 498 } 499 500 func (c *badDecoder) putError(buf []byte, err error) { 501 k := string(buf) 502 c.errorsLock.Lock() 503 defer c.errorsLock.Unlock() 504 c.errors[k] = err 505 } 506 507 func (c *badDecoder) Decode(buf []byte, o interface{}) error { 508 k := string(buf) 509 err := func() error { 510 c.errorsLock.RLock() 511 defer c.errorsLock.RUnlock() 512 return c.errors[k] 513 }() 514 if err != nil { 515 return err 516 } 517 return c.Codec.Decode(buf, o) 518 } 519 520 // TestBlockOpsReadyFailDecode checks that BlockOpsStandard.Get() 521 // fails if it can't decode the encrypted block. 522 func TestBlockOpsGetFailDecode(t *testing.T) { 523 ctx := context.Background() 524 config := makeTestBlockOpsConfig(t) 525 badDecoder := badDecoder{ 526 Codec: config.Codec(), 527 errors: make(map[string]error), 528 } 529 config.codec = &badDecoder 530 bops := NewBlockOpsStandard( 531 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 532 0, env.EmptyAppStateUpdater{}) 533 defer testBlockOpsShutdown(ctx, t, bops) 534 535 tlfID := tlf.FakeID(0, tlf.Private) 536 var latestKeyGen kbfsmd.KeyGen = 5 537 kmd := makeFakeKeyMetadata(tlfID, latestKeyGen) 538 539 id, _, readyBlockData, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 540 require.NoError(t, err) 541 542 decodeErr := errors.New("could not decode") 543 badDecoder.putError(readyBlockData.Buf, decodeErr) 544 545 bCtx := kbfsblock.MakeFirstContext( 546 keybase1.MakeTestUID(1).AsUserOrTeam(), keybase1.BlockType_DATA) 547 err = config.bserver.Put( 548 ctx, tlfID, id, bCtx, readyBlockData.Buf, readyBlockData.ServerHalf, 549 DiskBlockAnyCache) 550 require.NoError(t, err) 551 552 var decryptedBlock data.FileBlock 553 err = bops.Get(ctx, kmd, 554 data.BlockPointer{ID: id, DataVer: data.FirstValidVer, 555 KeyGen: latestKeyGen, Context: bCtx}, 556 &decryptedBlock, data.NoCacheEntry, data.MasterBranch) 557 require.Equal(t, decodeErr, err) 558 } 559 560 type badBlockDecryptor struct { 561 cryptoPure 562 } 563 564 func (c badBlockDecryptor) DecryptBlock( 565 _ kbfscrypto.EncryptedBlock, _ kbfscrypto.TLFCryptKey, 566 _ kbfscrypto.BlockCryptKeyServerHalf, _ data.Block) error { 567 return errors.New("could not decrypt block") 568 } 569 570 // TestBlockOpsReadyFailDecrypt checks that BlockOpsStandard.Get() 571 // fails if it can't decrypt the encrypted block. 572 func TestBlockOpsGetFailDecrypt(t *testing.T) { 573 ctx := context.Background() 574 config := makeTestBlockOpsConfig(t) 575 config.cp = badBlockDecryptor{config.cryptoPure()} 576 bops := NewBlockOpsStandard( 577 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 578 0, env.EmptyAppStateUpdater{}) 579 defer testBlockOpsShutdown(ctx, t, bops) 580 581 tlfID := tlf.FakeID(0, tlf.Private) 582 var latestKeyGen kbfsmd.KeyGen = 5 583 kmd := makeFakeKeyMetadata(tlfID, latestKeyGen) 584 585 id, _, readyBlockData, err := bops.Ready(ctx, kmd, &data.FileBlock{}) 586 require.NoError(t, err) 587 588 bCtx := kbfsblock.MakeFirstContext( 589 keybase1.MakeTestUID(1).AsUserOrTeam(), keybase1.BlockType_DATA) 590 err = config.bserver.Put( 591 ctx, tlfID, id, bCtx, readyBlockData.Buf, readyBlockData.ServerHalf, 592 DiskBlockAnyCache) 593 require.NoError(t, err) 594 595 var decryptedBlock data.FileBlock 596 err = bops.Get(ctx, kmd, 597 data.BlockPointer{ID: id, DataVer: data.FirstValidVer, 598 KeyGen: latestKeyGen, Context: bCtx}, 599 &decryptedBlock, data.NoCacheEntry, data.MasterBranch) 600 require.EqualError(t, err, "could not decrypt block") 601 } 602 603 func TestBlockOpsDeleteSuccess(t *testing.T) { 604 ctx := context.Background() 605 ctr := NewSafeTestReporter(t) 606 mockCtrl := gomock.NewController(ctr) 607 defer mockCtrl.Finish() 608 609 bserver := NewMockBlockServer(mockCtrl) 610 config := makeTestBlockOpsConfig(t) 611 config.bserver = bserver 612 bops := NewBlockOpsStandard( 613 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 614 0, env.EmptyAppStateUpdater{}) 615 defer testBlockOpsShutdown(ctx, t, bops) 616 617 // Expect one call to delete several blocks. 618 619 b1 := data.BlockPointer{ID: kbfsblock.FakeID(1)} 620 b2 := data.BlockPointer{ID: kbfsblock.FakeID(2)} 621 622 contexts := kbfsblock.ContextMap{ 623 b1.ID: {b1.Context}, 624 b2.ID: {b2.Context}, 625 } 626 627 expectedLiveCounts := map[kbfsblock.ID]int{ 628 b1.ID: 5, 629 b2.ID: 3, 630 } 631 632 tlfID := tlf.FakeID(1, tlf.Private) 633 bserver.EXPECT().RemoveBlockReferences(ctx, tlfID, contexts). 634 Return(expectedLiveCounts, nil) 635 636 liveCounts, err := bops.Delete(ctx, tlfID, []data.BlockPointer{b1, b2}) 637 require.NoError(t, err) 638 require.Equal(t, expectedLiveCounts, liveCounts) 639 } 640 641 func TestBlockOpsDeleteFail(t *testing.T) { 642 ctx := context.Background() 643 ctr := NewSafeTestReporter(t) 644 mockCtrl := gomock.NewController(ctr) 645 defer mockCtrl.Finish() 646 647 bserver := NewMockBlockServer(mockCtrl) 648 config := makeTestBlockOpsConfig(t) 649 config.bserver = bserver 650 bops := NewBlockOpsStandard( 651 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 652 0, env.EmptyAppStateUpdater{}) 653 defer testBlockOpsShutdown(ctx, t, bops) 654 655 b1 := data.BlockPointer{ID: kbfsblock.FakeID(1)} 656 b2 := data.BlockPointer{ID: kbfsblock.FakeID(2)} 657 658 contexts := kbfsblock.ContextMap{ 659 b1.ID: {b1.Context}, 660 b2.ID: {b2.Context}, 661 } 662 663 // Fail the delete call. 664 665 tlfID := tlf.FakeID(1, tlf.Private) 666 expectedErr := errors.New("Fake fail") 667 bserver.EXPECT().RemoveBlockReferences(ctx, tlfID, contexts). 668 Return(nil, expectedErr) 669 670 _, err := bops.Delete(ctx, tlfID, []data.BlockPointer{b1, b2}) 671 require.Equal(t, expectedErr, err) 672 } 673 674 func TestBlockOpsArchiveSuccess(t *testing.T) { 675 ctx := context.Background() 676 ctr := NewSafeTestReporter(t) 677 mockCtrl := gomock.NewController(ctr) 678 defer func() { 679 ctr.CheckForFailures() 680 mockCtrl.Finish() 681 }() 682 683 bserver := NewMockBlockServer(mockCtrl) 684 config := makeTestBlockOpsConfig(t) 685 config.bserver = bserver 686 bops := NewBlockOpsStandard( 687 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 688 0, env.EmptyAppStateUpdater{}) 689 defer testBlockOpsShutdown(ctx, t, bops) 690 691 // Expect one call to archive several blocks. 692 693 b1 := data.BlockPointer{ID: kbfsblock.FakeID(1)} 694 b2 := data.BlockPointer{ID: kbfsblock.FakeID(2)} 695 696 contexts := kbfsblock.ContextMap{ 697 b1.ID: {b1.Context}, 698 b2.ID: {b2.Context}, 699 } 700 701 tlfID := tlf.FakeID(1, tlf.Private) 702 bserver.EXPECT().ArchiveBlockReferences(ctx, tlfID, contexts). 703 Return(nil) 704 705 err := bops.Archive(ctx, tlfID, []data.BlockPointer{b1, b2}) 706 require.NoError(t, err) 707 } 708 709 func TestBlockOpsArchiveFail(t *testing.T) { 710 ctx := context.Background() 711 ctr := NewSafeTestReporter(t) 712 mockCtrl := gomock.NewController(ctr) 713 defer func() { 714 ctr.CheckForFailures() 715 mockCtrl.Finish() 716 }() 717 718 bserver := NewMockBlockServer(mockCtrl) 719 config := makeTestBlockOpsConfig(t) 720 config.bserver = bserver 721 bops := NewBlockOpsStandard( 722 config, testBlockRetrievalWorkerQueueSize, testPrefetchWorkerQueueSize, 723 0, env.EmptyAppStateUpdater{}) 724 defer testBlockOpsShutdown(ctx, t, bops) 725 726 b1 := data.BlockPointer{ID: kbfsblock.FakeID(1)} 727 b2 := data.BlockPointer{ID: kbfsblock.FakeID(2)} 728 729 contexts := kbfsblock.ContextMap{ 730 b1.ID: {b1.Context}, 731 b2.ID: {b2.Context}, 732 } 733 734 // Fail the archive call. 735 736 tlfID := tlf.FakeID(1, tlf.Private) 737 expectedErr := errors.New("Fake fail") 738 bserver.EXPECT().ArchiveBlockReferences(ctx, tlfID, contexts). 739 Return(expectedErr) 740 741 err := bops.Archive(ctx, tlfID, []data.BlockPointer{b1, b2}) 742 require.Equal(t, expectedErr, err) 743 }