From dcf36e71ca6e3597f21bb64c660005f275c30ac8 Mon Sep 17 00:00:00 2001 From: Islam Aleiv Date: Tue, 13 Aug 2024 10:56:00 +0200 Subject: [PATCH] Store enc key CID in a block instead of height --- crypto/cid.go | 26 ++++ internal/core/block/block.go | 12 +- internal/core/block/block_test.go | 14 +- internal/core/block/errors.go | 24 ++-- internal/core/key.go | 13 +- internal/db/merge.go | 33 +++-- internal/encryption/encryptor.go | 118 +++++++++++++---- internal/encryption/encryptor_test.go | 80 ++++-------- internal/merkle/clock/clock.go | 48 ++++--- internal/merkle/clock/errors.go | 2 + net/pb/net.pb.go | 134 ++++++++++---------- net/pb/net.proto | 4 +- net/pb/net_vtproto.pb.go | 36 ++++-- net/server_encryption_key.go | 31 +++-- tests/integration/encryption/commit_test.go | 10 +- tests/integration/encryption/peer_test.go | 10 +- 16 files changed, 345 insertions(+), 250 deletions(-) create mode 100644 crypto/cid.go diff --git a/crypto/cid.go b/crypto/cid.go new file mode 100644 index 0000000000..a7cc4a952d --- /dev/null +++ b/crypto/cid.go @@ -0,0 +1,26 @@ +// Copyright 2024 Democratized Data Foundation +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package crypto + +import ( + "github.com/ipfs/go-cid" + "github.com/multiformats/go-multihash" +) + +// GenerateCid generates a CID from the given data. +func GenerateCid(data []byte) (cid.Cid, error) { + mh, err := multihash.Sum(data, multihash.SHA2_256, -1) + if err != nil { + return cid.Cid{}, err + } + + return cid.NewCidV1(cid.Raw, mh), nil +} diff --git a/internal/core/block/block.go b/internal/core/block/block.go index c7527f7170..de6ddcdebb 100644 --- a/internal/core/block/block.go +++ b/internal/core/block/block.go @@ -112,8 +112,8 @@ const ( type Encryption struct { // Type indicates on what level encryption is applied. Type EncryptionType - // From specifies the block height from which the encryption is applied. - From uint64 + // CID of the key used for encryption. + KeyID []byte } // Block is a block that contains a CRDT delta and links to other blocks. @@ -129,7 +129,7 @@ type Block struct { // IsEncrypted returns true if the block is encrypted. func (b *Block) IsEncrypted() bool { - return b.Encryption != nil && (*b.Encryption).Type != NotEncrypted + return b.Encryption != nil && b.Encryption.Type != NotEncrypted } // GetPrevBlockCid returns the CID of the previous block. @@ -155,7 +155,7 @@ func (b Block) IPLDSchemaBytes() []byte { type Encryption struct { type EncryptionType - from Int + keyID Bytes } type EncryptionType enum { @@ -298,8 +298,8 @@ func (b *Block) Validate() error { return ErrInvalidBlockEncryptionType } - if (*b.Encryption).From == 0 { - return ErrInvalidBlockEncryptionFrom + if len(b.Encryption.KeyID) == 0 { + return ErrInvalidBlockEncryptionKeyID } } return nil diff --git a/internal/core/block/block_test.go b/internal/core/block/block_test.go index 3c5a374ed9..bfd949077a 100644 --- a/internal/core/block/block_test.go +++ b/internal/core/block/block_test.go @@ -237,17 +237,17 @@ func TestBlock_Validate(t *testing.T) { }{ { name: "NotEncrypted type is valid", - encryption: &Encryption{Type: NotEncrypted, From: 1}, + encryption: &Encryption{Type: NotEncrypted, KeyID: []byte{1}}, expectedError: nil, }, { name: "DocumentEncrypted type is valid", - encryption: &Encryption{Type: DocumentEncrypted, From: 1}, + encryption: &Encryption{Type: DocumentEncrypted, KeyID: []byte{1}}, expectedError: nil, }, { name: "FieldEncrypted type is valid", - encryption: &Encryption{Type: FieldEncrypted, From: 1}, + encryption: &Encryption{Type: FieldEncrypted, KeyID: []byte{1}}, expectedError: nil, }, { @@ -257,13 +257,13 @@ func TestBlock_Validate(t *testing.T) { }, { name: "Invalid encryption type", - encryption: &Encryption{Type: EncryptionType(99), From: 1}, + encryption: &Encryption{Type: EncryptionType(99), KeyID: []byte{1}}, expectedError: ErrInvalidBlockEncryptionType, }, { - name: "Invalid encryption from parameter", - encryption: &Encryption{Type: DocumentEncrypted}, - expectedError: ErrInvalidBlockEncryptionFrom, + name: "Invalid encryption key id parameter", + encryption: &Encryption{Type: DocumentEncrypted, KeyID: []byte{}}, + expectedError: ErrInvalidBlockEncryptionKeyID, }, } diff --git a/internal/core/block/errors.go b/internal/core/block/errors.go index c45e2b0cca..ced4c4d6a1 100644 --- a/internal/core/block/errors.go +++ b/internal/core/block/errors.go @@ -17,12 +17,12 @@ import ( ) const ( - errNodeToBlock string = "failed to convert node to block" - errEncodingBlock string = "failed to encode block" - errUnmarshallingBlock string = "failed to unmarshal block" - errGeneratingLink string = "failed to generate link" - errInvalidBlockEncryptionType string = "invalid block encryption type" - errInvalidBlockEncryptionFrom string = "invalid block encryption from parameter" + errNodeToBlock string = "failed to convert node to block" + errEncodingBlock string = "failed to encode block" + errUnmarshallingBlock string = "failed to unmarshal block" + errGeneratingLink string = "failed to generate link" + errInvalidBlockEncryptionType string = "invalid block encryption type" + errInvalidBlockEncryptionKeyID string = "invalid block encryption key id" ) // Errors returnable from this package. @@ -30,12 +30,12 @@ const ( // This list is incomplete and undefined errors may also be returned. // Errors returned from this package may be tested against these errors with errors.Is. var ( - ErrNodeToBlock = errors.New(errNodeToBlock) - ErrEncodingBlock = errors.New(errEncodingBlock) - ErrUnmarshallingBlock = errors.New(errUnmarshallingBlock) - ErrGeneratingLink = errors.New(errGeneratingLink) - ErrInvalidBlockEncryptionType = errors.New(errInvalidBlockEncryptionType) - ErrInvalidBlockEncryptionFrom = errors.New(errInvalidBlockEncryptionFrom) + ErrNodeToBlock = errors.New(errNodeToBlock) + ErrEncodingBlock = errors.New(errEncodingBlock) + ErrUnmarshallingBlock = errors.New(errUnmarshallingBlock) + ErrGeneratingLink = errors.New(errGeneratingLink) + ErrInvalidBlockEncryptionType = errors.New(errInvalidBlockEncryptionType) + ErrInvalidBlockEncryptionKeyID = errors.New(errInvalidBlockEncryptionKeyID) ) // NewErrFailedToGetPriority returns an error indicating that the priority could not be retrieved. diff --git a/internal/core/key.go b/internal/core/key.go index bdddfb2478..a61e6ac854 100644 --- a/internal/core/key.go +++ b/internal/core/key.go @@ -800,9 +800,8 @@ type EncStoreDocKey struct { // FieldName is the name of the field that the key is for. // If unset, it indicates that the key is for the whole document. FieldName immutable.Option[string] - // BlockHeight is the height of the block that the key is for. - // It is used to differentiate keys that are used in different point in time. - BlockHeight uint64 + // KeyID is a hash (Cid) of the of the encryption key. + KeyID string } var _ Key = (*EncStoreDocKey)(nil) @@ -810,15 +809,15 @@ var _ Key = (*EncStoreDocKey)(nil) // NewEncStoreDocKey creates a new EncStoreDocKey from a docID and fieldID. // Unset fieldName indicates that the key is for the whole document. // blockHeight is the height of the block that the key is for. -func NewEncStoreDocKey(docID string, fieldName immutable.Option[string], blockHeight uint64) EncStoreDocKey { - return EncStoreDocKey{DocID: docID, FieldName: fieldName, BlockHeight: blockHeight} +func NewEncStoreDocKey(docID string, fieldName immutable.Option[string], keyID string) EncStoreDocKey { + return EncStoreDocKey{DocID: docID, FieldName: fieldName, KeyID: keyID} } func (k EncStoreDocKey) ToString() string { if k.FieldName.HasValue() { - return fmt.Sprintf("%s/%s/%d", k.DocID, k.FieldName.Value(), k.BlockHeight) + return fmt.Sprintf("%s/%s/%s", k.DocID, k.FieldName.Value(), k.KeyID) } - return fmt.Sprintf("%s/%d", k.DocID, k.BlockHeight) + return fmt.Sprintf("%s/%s", k.DocID, k.KeyID) } func (k EncStoreDocKey) Bytes() []byte { diff --git a/internal/db/merge.go b/internal/db/merge.go index 27def3d213..7052f616d0 100644 --- a/internal/db/merge.go +++ b/internal/db/merge.go @@ -11,6 +11,7 @@ package db import ( + "bytes" "container/list" "context" "sync" @@ -153,7 +154,7 @@ func (db *db) mergeEncryptedBlocks(ctx context.Context, keyEvent encryption.KeyR return err } - blocks, err = loadBlocksChainFromBlockstoreTillHeight(ctx, txn, cids, mergeGroup.compositeKey.BlockHeight) + blocks, err = loadBlocksWithKeyIDFromBlockstore(ctx, txn, cids, mergeGroup.compositeKey.KeyID) if err != nil { return err } @@ -171,7 +172,7 @@ func (db *db) mergeEncryptedBlocks(ctx context.Context, keyEvent encryption.KeyR return err } - fieldBlocks, err := loadBlocksChainFromBlockstoreTillHeight(ctx, txn, cids, fieldStoreKey.BlockHeight) + fieldBlocks, err := loadBlocksWithKeyIDFromBlockstore(ctx, txn, cids, fieldStoreKey.KeyID) if err != nil { return err } @@ -387,7 +388,7 @@ func (mp *mergeProcessor) processEncryptedBlock( if blockEnc.Type == coreblock.FieldEncrypted { fieldName = immutable.Some(dagBlock.Delta.GetFieldName()) } - mp.addPendingEncryptionRequest(docID, fieldName, blockEnc.From) + mp.addPendingEncryptionRequest(docID, fieldName, string(blockEnc.KeyID)) } return dagBlock, true, nil } @@ -395,8 +396,8 @@ func (mp *mergeProcessor) processEncryptedBlock( return dagBlock, false, nil } -func (mp *mergeProcessor) addPendingEncryptionRequest(docID string, fieldName immutable.Option[string], height uint64) { - mp.pendingEncryptionKeyRequests[core.NewEncStoreDocKey(docID, fieldName, height)] = struct{}{} +func (mp *mergeProcessor) addPendingEncryptionRequest(docID string, fieldName immutable.Option[string], keyID string) { + mp.pendingEncryptionKeyRequests[core.NewEncStoreDocKey(docID, fieldName, keyID)] = struct{}{} if !fieldName.HasValue() { mp.hasPendingCompositeBlock = true } @@ -470,7 +471,7 @@ func decryptBlock(ctx context.Context, block *coreblock.Block) (*coreblock.Block optFieldName = immutable.Some(block.Delta.GetFieldName()) } - encStoreKey := core.NewEncStoreDocKey(string(block.Delta.GetDocID()), optFieldName, blockEnc.From) + encStoreKey := core.NewEncStoreDocKey(string(block.Delta.GetDocID()), optFieldName, string(blockEnc.KeyID)) if block.Delta.IsComposite() { // for composite blocks there is nothing to decrypt @@ -609,14 +610,14 @@ func loadBlockFromBlockStore(ctx context.Context, txn datastore.Txn, cid cid.Cid return block, nil } -// loadBlocksChainFromBlockstoreTillHeight loads the blocks from the blockstore starting from the given CIDs -// until it reaches a block with a height equal to the given height (including that block). -// The returned blocks are ordered from the highest height to the lowest. -func loadBlocksChainFromBlockstoreTillHeight( +// loadBlocksWithKeyIDFromBlockstore loads the blocks from the blockstore that have given encryption +// keyID until it reaches a block with a different keyID or without any. +// The returned blocks are ordered from the newest to the oldest. +func loadBlocksWithKeyIDFromBlockstore( ctx context.Context, txn datastore.Txn, cids []cid.Cid, - height uint64, + keyID string, ) ([]*coreblock.Block, error) { var blocks []*coreblock.Block for len(cids) > 0 { @@ -626,13 +627,11 @@ func loadBlocksChainFromBlockstoreTillHeight( return nil, err } - if block.Delta.GetPriority() >= height { + if block.Encryption != nil && bytes.Equal(block.Encryption.KeyID, []byte(keyID)) { blocks = append(blocks, block) - if block.Delta.GetPriority() != height { - prevCid := block.GetPrevBlockCid() - if prevCid.HasValue() { - cids = append(cids, prevCid.Value()) - } + prevCid := block.GetPrevBlockCid() + if prevCid.HasValue() { + cids = append(cids, prevCid.Value()) } } cids = cids[1:] diff --git a/internal/encryption/encryptor.go b/internal/encryption/encryptor.go index 72f44dec01..ebab69262c 100644 --- a/internal/encryption/encryptor.go +++ b/internal/encryption/encryptor.go @@ -53,10 +53,11 @@ func generateTestEncryptionKey(docID string, fieldName immutable.Option[string]) // It acts based on the configuration [DocEncConfig] provided and data stored in the provided store. // It uses [core.EncStoreDocKey] to store and retrieve encryption keys. type DocEncryptor struct { - conf immutable.Option[DocEncConfig] - ctx context.Context - store datastore.DSReaderWriter - cache map[core.EncStoreDocKey][]byte + conf immutable.Option[DocEncConfig] + ctx context.Context + store datastore.DSReaderWriter + cache map[core.EncStoreDocKey][]byte + generatedKeys []core.EncStoreDocKey } func newDocEncryptor(ctx context.Context) *DocEncryptor { @@ -103,9 +104,8 @@ func shouldEncryptDocField(conf immutable.Option[DocEncConfig], fieldName immuta return false } -// Encrypt encrypts the given plainText that is associated with the given docID, fieldName and block height. -// If the current configuration is set to encrypt the given key individually, it will encrypt it with a new key. -// Otherwise, it will use document-level encryption key. +// Encrypt encrypts the given plainText with the encryption key that is associated with the given docID, +// fieldName and key id. func (d *DocEncryptor) Encrypt( encStoreKey core.EncStoreDocKey, plainText []byte, @@ -113,31 +113,17 @@ func (d *DocEncryptor) Encrypt( if d.store == nil { return nil, ErrNoStorageProvided } + encryptionKey, err := d.fetchByEncStoreKey(encStoreKey) if err != nil { return nil, err } - if len(encryptionKey) == 0 { - if !shouldEncryptIndividualField(d.conf, encStoreKey.FieldName) { - encStoreKey.FieldName = immutable.None[string]() - } - - if !shouldEncryptDocField(d.conf, encStoreKey.FieldName) { - return plainText, nil - } - - encryptionKey, err = generateEncryptionKeyFunc(encStoreKey.DocID, encStoreKey.FieldName) - if err != nil { - return nil, err - } - - err = d.storeByEncStoreKey(encStoreKey, encryptionKey) - if err != nil { - return nil, err - } + var cipherText []byte + if len(plainText) > 0 { + cipherText, _, err = crypto.EncryptAES(plainText, encryptionKey, nil, true) } - cipherText, _, err := crypto.EncryptAES(plainText, encryptionKey, nil, true) + return cipherText, err } @@ -194,6 +180,68 @@ func (d *DocEncryptor) GetKey(encStoreKey core.EncStoreDocKey) ([]byte, error) { return encryptionKey, nil } +// getGeneratedKeyFor returns the generated key for the given docID and fieldName. +func (d *DocEncryptor) getGeneratedKeyFor( + docID string, + fieldName immutable.Option[string], +) (immutable.Option[core.EncStoreDocKey], []byte) { + for _, key := range d.generatedKeys { + if key.DocID == docID && key.FieldName == fieldName { + return immutable.Some(key), d.cache[key] + } + } + return immutable.None[core.EncStoreDocKey](), nil +} + +// GetOrGenerateEncryptionKey returns the generated encryption key for the given docID, (optional) fieldName. +// If the key is not generated before, it generates a new key and stores it. +func (d *DocEncryptor) GetOrGenerateEncryptionKey( + docID string, + fieldName immutable.Option[string], +) (immutable.Option[core.EncStoreDocKey], []byte, error) { + encStoreKey, encryptionKey := d.getGeneratedKeyFor(docID, fieldName) + if encStoreKey.HasValue() { + return encStoreKey, encryptionKey, nil + } + + return d.generateEncryptionKey(docID, fieldName) +} + +// generateEncryptionKey generates a new encryption key for the given docID and fieldName. +func (d *DocEncryptor) generateEncryptionKey( + docID string, + fieldName immutable.Option[string], +) (immutable.Option[core.EncStoreDocKey], []byte, error) { + encStoreKey := core.NewEncStoreDocKey(docID, fieldName, "") + if !shouldEncryptIndividualField(d.conf, fieldName) { + encStoreKey.FieldName = immutable.None[string]() + } + + if !shouldEncryptDocField(d.conf, encStoreKey.FieldName) { + return immutable.None[core.EncStoreDocKey](), nil, nil + } + + encryptionKey, err := generateEncryptionKeyFunc(encStoreKey.DocID, encStoreKey.FieldName) + if err != nil { + return immutable.None[core.EncStoreDocKey](), nil, err + } + + keyID, err := crypto.GenerateCid(encryptionKey) + if err != nil { + return immutable.None[core.EncStoreDocKey](), nil, err + } + encStoreKey.KeyID = keyID.String() + + err = d.storeByEncStoreKey(encStoreKey, encryptionKey) + if err != nil { + return immutable.None[core.EncStoreDocKey](), nil, err + } + + d.generatedKeys = append(d.generatedKeys, encStoreKey) + + return immutable.Some(encStoreKey), encryptionKey, nil +} + // SaveKey saves the given encryption key for the given docID, (optional) fieldName and block height. func (d *DocEncryptor) SaveKey(encStoreKey core.EncStoreDocKey, encryptionKey []byte) error { if d.store == nil { @@ -265,11 +313,27 @@ func GetKey(ctx context.Context, encStoreKey core.EncStoreDocKey) ([]byte, error return enc.GetKey(encStoreKey) } +// GetOrGenerateEncryptionKey returns the generated encryption key for the given docID, (optional) fieldName. +// If the key is not generated before, it generates a new key and stores it. +func GetOrGenerateEncryptionKey( + ctx context.Context, + docID string, + fieldName immutable.Option[string], +) (immutable.Option[core.EncStoreDocKey], []byte, error) { + enc, ok := TryGetContextEncryptor(ctx) + if !ok { + return immutable.None[core.EncStoreDocKey](), nil, nil + } + return enc.GetOrGenerateEncryptionKey(docID, fieldName) +} + func init() { arg := os.Args[0] // If the binary is a test binary, use a deterministic nonce. // TODO: We should try to find a better way to detect this https://github.com/sourcenetwork/defradb/issues/2801 - if strings.HasSuffix(arg, ".test") || strings.Contains(arg, "/defradb/tests/") { + if strings.HasSuffix(arg, ".test") || + strings.Contains(arg, "/defradb/tests/") || + strings.Contains(arg, "/__debug_bin") { generateEncryptionKeyFunc = generateTestEncryptionKey } } diff --git a/internal/encryption/encryptor_test.go b/internal/encryption/encryptor_test.go index e89a573c92..2b979d1dec 100644 --- a/internal/encryption/encryptor_test.go +++ b/internal/encryption/encryptor_test.go @@ -15,7 +15,6 @@ import ( "errors" "testing" - ds "github.com/ipfs/go-datastore" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -29,7 +28,6 @@ import ( var testErr = errors.New("test error") const docID = "bae-c9fb0fa4-1195-589c-aa54-e68333fb90b3" -const height = 1 var fieldName = immutable.Some("name") var noFieldName = immutable.None[string]() @@ -43,6 +41,16 @@ func getEncKey(fieldName immutable.Option[string]) []byte { return key } +func getKeyID(fieldName immutable.Option[string]) string { + key := getEncKey(fieldName) + cid, _ := crypto.GenerateCid(key) + return cid.String() +} + +func makeStoreKey(docID string, fieldName immutable.Option[string]) core.EncStoreDocKey { + return core.NewEncStoreDocKey(docID, fieldName, getKeyID(fieldName)) +} + func getCipherText(t *testing.T, fieldName immutable.Option[string]) []byte { cipherText, _, err := crypto.EncryptAES(getPlainText(), getEncKey(fieldName), nil, true) assert.NoError(t, err) @@ -66,7 +74,7 @@ func TestEncryptorEncrypt_IfStorageReturnsError_Error(t *testing.T) { st.EXPECT().Get(mock.Anything, mock.Anything).Return(nil, testErr) - _, err := enc.Encrypt(core.NewEncStoreDocKey(docID, fieldName, height), []byte("test")) + _, err := enc.Encrypt(makeStoreKey(docID, fieldName), []byte("test")) assert.ErrorIs(t, err, testErr) } @@ -74,53 +82,26 @@ func TestEncryptorEncrypt_IfStorageReturnsError_Error(t *testing.T) { func TestEncryptorEncrypt_WithEmptyFieldNameIfNoKeyFoundInStorage_ShouldGenerateKeyStoreItAndReturnCipherText(t *testing.T) { enc, st := newDefaultEncryptor(t) - storeKey := core.NewEncStoreDocKey(docID, noFieldName, height) + storeKey := makeStoreKey(docID, noFieldName) - st.EXPECT().Get(mock.Anything, storeKey.ToDS()).Return(nil, ds.ErrNotFound) st.EXPECT().Put(mock.Anything, storeKey.ToDS(), getEncKey(noFieldName)).Return(nil) - cipherText, err := enc.Encrypt(core.NewEncStoreDocKey(docID, noFieldName, height), getPlainText()) - + _, _, err := enc.GetOrGenerateEncryptionKey(docID, noFieldName) assert.NoError(t, err) - assert.Equal(t, getCipherText(t, noFieldName), cipherText) -} -func TestEncryptorEncrypt_IfNoFieldEncRequestedAndNoKeyInStorage_GenerateKeyStoreItAndReturnCipherText(t *testing.T) { - enc, st := newDefaultEncryptor(t) - - docStoreKey := core.NewEncStoreDocKey(docID, noFieldName, height).ToDS() - fieldStoreKey := core.NewEncStoreDocKey(docID, fieldName, height).ToDS() - - st.EXPECT().Get(mock.Anything, fieldStoreKey).Return(nil, ds.ErrNotFound) - st.EXPECT().Put(mock.Anything, docStoreKey, getEncKey(noFieldName)).Return(nil) - - cipherText, err := enc.Encrypt(core.NewEncStoreDocKey(docID, fieldName, height), getPlainText()) + cipherText, err := enc.Encrypt(makeStoreKey(docID, noFieldName), getPlainText()) assert.NoError(t, err) assert.Equal(t, getCipherText(t, noFieldName), cipherText) } -func TestEncryptorEncrypt_IfNoKeyWithFieldFoundInStorage_ShouldGenerateKeyStoreItAndReturnCipherText(t *testing.T) { - enc, st := newEncryptorWithConfig(t, DocEncConfig{EncryptedFields: []string{fieldName.Value()}}) - - storeKey := core.NewEncStoreDocKey(docID, fieldName, height) - - st.EXPECT().Get(mock.Anything, storeKey.ToDS()).Return(nil, ds.ErrNotFound) - st.EXPECT().Put(mock.Anything, storeKey.ToDS(), getEncKey(fieldName)).Return(nil) - - cipherText, err := enc.Encrypt(core.NewEncStoreDocKey(docID, fieldName, height), getPlainText()) - - assert.NoError(t, err) - assert.Equal(t, getCipherText(t, fieldName), cipherText) -} - func TestEncryptorEncrypt_IfKeyWithFieldFoundInStorage_ShouldUseItToReturnCipherText(t *testing.T) { enc, st := newEncryptorWithConfig(t, DocEncConfig{EncryptedFields: []string{fieldName.Value()}}) - storeKey := core.NewEncStoreDocKey(docID, fieldName, height) + storeKey := makeStoreKey(docID, fieldName) st.EXPECT().Get(mock.Anything, storeKey.ToDS()).Return(getEncKey(fieldName), nil) - cipherText, err := enc.Encrypt(core.NewEncStoreDocKey(docID, fieldName, height), getPlainText()) + cipherText, err := enc.Encrypt(makeStoreKey(docID, fieldName), getPlainText()) assert.NoError(t, err) assert.Equal(t, getCipherText(t, fieldName), cipherText) @@ -131,7 +112,7 @@ func TestEncryptorEncrypt_IfKeyFoundInStorage_ShouldUseItToReturnCipherText(t *t st.EXPECT().Get(mock.Anything, mock.Anything).Return(getEncKey(noFieldName), nil) - cipherText, err := enc.Encrypt(core.NewEncStoreDocKey(docID, noFieldName, height), getPlainText()) + cipherText, err := enc.Encrypt(makeStoreKey(docID, noFieldName), getPlainText()) assert.NoError(t, err) assert.Equal(t, getCipherText(t, noFieldName), cipherText) @@ -140,32 +121,27 @@ func TestEncryptorEncrypt_IfKeyFoundInStorage_ShouldUseItToReturnCipherText(t *t func TestEncryptorEncrypt_IfStorageFailsToStoreEncryptionKey_ReturnError(t *testing.T) { enc, st := newDefaultEncryptor(t) - st.EXPECT().Get(mock.Anything, mock.Anything).Return(nil, ds.ErrNotFound) - st.EXPECT().Put(mock.Anything, mock.Anything, mock.Anything).Return(testErr) - _, err := enc.Encrypt(core.NewEncStoreDocKey(docID, fieldName, height), getPlainText()) - + _, _, err := enc.GetOrGenerateEncryptionKey(docID, noFieldName) assert.ErrorIs(t, err, testErr) } func TestEncryptorEncrypt_IfKeyGenerationIsNotEnabled_ShouldReturnPlainText(t *testing.T) { - enc, st := newDefaultEncryptor(t) + enc, _ := newDefaultEncryptor(t) enc.SetConfig(immutable.None[DocEncConfig]()) - st.EXPECT().Get(mock.Anything, mock.Anything).Return(nil, ds.ErrNotFound) - - cipherText, err := enc.Encrypt(core.NewEncStoreDocKey(docID, fieldName, height), getPlainText()) - + encStoreKey, encryptionKey, err := enc.GetOrGenerateEncryptionKey(docID, noFieldName) assert.NoError(t, err) - assert.Equal(t, getPlainText(), cipherText) + assert.Len(t, encryptionKey, 0) + assert.Equal(t, encStoreKey, immutable.None[core.EncStoreDocKey]()) } func TestEncryptorEncrypt_IfNoStorageProvided_Error(t *testing.T) { enc, _ := newDefaultEncryptor(t) enc.SetStore(nil) - _, err := enc.Encrypt(core.NewEncStoreDocKey(docID, fieldName, height), getPlainText()) + _, err := enc.Encrypt(makeStoreKey(docID, fieldName), getPlainText()) assert.ErrorIs(t, err, ErrNoStorageProvided) } @@ -174,7 +150,7 @@ func TestEncryptorDecrypt_IfNoStorageProvided_Error(t *testing.T) { enc, _ := newDefaultEncryptor(t) enc.SetStore(nil) - _, err := enc.Decrypt(core.NewEncStoreDocKey(docID, fieldName, height), getPlainText()) + _, err := enc.Decrypt(makeStoreKey(docID, fieldName), getPlainText()) assert.ErrorIs(t, err, ErrNoStorageProvided) } @@ -184,7 +160,7 @@ func TestEncryptorDecrypt_IfStorageReturnsError_Error(t *testing.T) { st.EXPECT().Get(mock.Anything, mock.Anything).Return(nil, testErr) - _, err := enc.Decrypt(core.NewEncStoreDocKey(docID, fieldName, height), []byte("test")) + _, err := enc.Decrypt(makeStoreKey(docID, fieldName), []byte("test")) assert.ErrorIs(t, err, testErr) } @@ -194,14 +170,14 @@ func TestEncryptorDecrypt_IfKeyFoundInStorage_ShouldUseItToReturnPlainText(t *te st.EXPECT().Get(mock.Anything, mock.Anything).Return(getEncKey(noFieldName), nil) - plainText, err := enc.Decrypt(core.NewEncStoreDocKey(docID, fieldName, height), getCipherText(t, noFieldName)) + plainText, err := enc.Decrypt(makeStoreKey(docID, fieldName), getCipherText(t, noFieldName)) assert.NoError(t, err) assert.Equal(t, getPlainText(), plainText) } func TestEncryptDoc_IfContextHasNoEncryptor_ReturnNil(t *testing.T) { - data, err := EncryptDoc(context.Background(), core.NewEncStoreDocKey(docID, fieldName, height), getPlainText()) + data, err := EncryptDoc(context.Background(), makeStoreKey(docID, fieldName), getPlainText()) assert.Nil(t, data, "data should be nil") assert.NoError(t, err, "error should be nil") } @@ -209,7 +185,7 @@ func TestEncryptDoc_IfContextHasNoEncryptor_ReturnNil(t *testing.T) { func TestDecryptDoc_IfContextHasNoEncryptor_ReturnNil(t *testing.T) { data, err := DecryptDoc( context.Background(), - core.NewEncStoreDocKey(docID, fieldName, height), + makeStoreKey(docID, fieldName), getCipherText(t, fieldName), ) assert.Nil(t, data, "data should be nil") diff --git a/internal/merkle/clock/clock.go b/internal/merkle/clock/clock.go index 2dec134d14..3d24138ca7 100644 --- a/internal/merkle/clock/clock.go +++ b/internal/merkle/clock/clock.go @@ -91,20 +91,16 @@ func (mc *MerkleClock) AddDelta( if block.Delta.GetFieldName() != "" { fieldName = immutable.Some(block.Delta.GetFieldName()) } - blockEnc, err := mc.determineBlockEncryptionData(ctx, fieldName, height, heads) + blockEnc, err := mc.determineBlockEncryptionData(ctx, string(block.Delta.GetDocID()), fieldName, heads) if err != nil { return cidlink.Link{}, nil, err } dagBlock := block if blockEnc != nil && blockEnc.Type != coreblock.NotEncrypted { - if !block.Delta.IsComposite() { - dagBlock, err = encryptBlock(ctx, block, blockEnc) - if err != nil { - return cidlink.Link{}, nil, err - } - } else { - dagBlock.Encryption = blockEnc + dagBlock, err = encryptBlock(ctx, block, blockEnc) + if err != nil { + return cidlink.Link{}, nil, err } } @@ -129,18 +125,25 @@ func (mc *MerkleClock) AddDelta( func (mc *MerkleClock) determineBlockEncryptionData( ctx context.Context, + docID string, fieldName immutable.Option[string], - height uint64, heads []cid.Cid, ) (*coreblock.Encryption, error) { // if new encryption was requested by the user if encryption.ShouldEncryptDocField(ctx, fieldName) { - blockEnc := &coreblock.Encryption{From: height} + blockEnc := &coreblock.Encryption{} if encryption.ShouldEncryptIndividualField(ctx, fieldName) { blockEnc.Type = coreblock.FieldEncrypted } else { blockEnc.Type = coreblock.DocumentEncrypted } + encStoreKey, _, err := encryption.GetOrGenerateEncryptionKey(ctx, docID, fieldName) + if err != nil { + return nil, err + } + if encStoreKey.HasValue() { + blockEnc.KeyID = []byte(encStoreKey.Value().KeyID) + } return blockEnc, nil } @@ -155,9 +158,10 @@ func (mc *MerkleClock) determineBlockEncryptionData( return nil, err } if prevBlock.Encryption != nil { - blockEnc := &coreblock.Encryption{Type: (*prevBlock.Encryption).Type} - blockEnc.From = prevBlock.Encryption.From - return blockEnc, nil + return &coreblock.Encryption{ + Type: prevBlock.Encryption.Type, + KeyID: prevBlock.Encryption.KeyID, + }, nil } } @@ -169,16 +173,20 @@ func encryptBlock( block *coreblock.Block, blockEnc *coreblock.Encryption, ) (*coreblock.Block, error) { - clonedCRDT := block.Delta.Clone() fieldName := immutable.None[string]() if blockEnc.Type == coreblock.FieldEncrypted { - fieldName = immutable.Some(clonedCRDT.GetFieldName()) + fieldName = immutable.Some(block.Delta.GetFieldName()) } - bytes, err := encryption.EncryptDoc( - ctx, - core.NewEncStoreDocKey(string(clonedCRDT.GetDocID()), fieldName, blockEnc.From), - clonedCRDT.GetData(), - ) + + encStoreKey := core.NewEncStoreDocKey(string(block.Delta.GetDocID()), fieldName, string(blockEnc.KeyID)) + blockEnc.KeyID = []byte(encStoreKey.KeyID) + if block.Delta.IsComposite() { + block.Encryption = blockEnc + return block, nil + } + + clonedCRDT := block.Delta.Clone() + bytes, err := encryption.EncryptDoc(ctx, encStoreKey, clonedCRDT.GetData()) if err != nil { return nil, err } diff --git a/internal/merkle/clock/errors.go b/internal/merkle/clock/errors.go index 9903f777a9..a20ce30731 100644 --- a/internal/merkle/clock/errors.go +++ b/internal/merkle/clock/errors.go @@ -26,6 +26,7 @@ const ( errReplacingHead = "error replacing head" errCouldNotFindBlock = "error checking for known block " errFailedToGetNextQResult = "failed to get next query result" + errCouldNotGetEncKey = "could not get encryption key" ) var ( @@ -39,6 +40,7 @@ var ( ErrCouldNotFindBlock = errors.New(errCouldNotFindBlock) ErrFailedToGetNextQResult = errors.New(errFailedToGetNextQResult) ErrDecodingHeight = errors.New("error decoding height") + ErrCouldNotGetEncKey = errors.New(errCouldNotGetEncKey) ) func NewErrCreatingBlock(inner error) error { diff --git a/net/pb/net.pb.go b/net/pb/net.pb.go index ec09f861a4..125e58f286 100644 --- a/net/pb/net.pb.go +++ b/net/pb/net.pb.go @@ -355,8 +355,8 @@ type EncryptionKeyTarget struct { // fieldName if the name of the document field that the key is being requested for. // If the fieldName is empty, the key is being requested for the whole document. FieldName string `protobuf:"bytes,2,opt,name=fieldName,proto3" json:"fieldName,omitempty"` - // height is the height of the block the encryption key is being requested for. - Height uint64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"` + // keyID is the hash (Cid) of the key that is being requested. + KeyID []byte `protobuf:"bytes,3,opt,name=keyID,proto3" json:"keyID,omitempty"` } func (x *EncryptionKeyTarget) Reset() { @@ -405,11 +405,11 @@ func (x *EncryptionKeyTarget) GetFieldName() string { return "" } -func (x *EncryptionKeyTarget) GetHeight() uint64 { +func (x *EncryptionKeyTarget) GetKeyID() []byte { if x != nil { - return x.Height + return x.KeyID } - return 0 + return nil } // FetchEncryptionKeyRequest is a request to receive a doc encryption key @@ -801,72 +801,72 @@ var file_net_proto_rawDesc = []byte{ 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x1d, 0x0a, 0x03, 0x6c, 0x6f, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x4c, 0x6f, 0x67, 0x52, - 0x03, 0x6c, 0x6f, 0x67, 0x22, 0x61, 0x0a, 0x13, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, + 0x03, 0x6c, 0x6f, 0x67, 0x22, 0x5f, 0x0a, 0x13, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x64, 0x6f, 0x63, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x64, 0x6f, 0x63, 0x49, 0x44, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, 0xc0, 0x01, 0x0a, 0x19, 0x46, 0x65, 0x74, 0x63, - 0x68, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, - 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x54, 0x61, 0x72, - 0x67, 0x65, 0x74, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, - 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2e, 0x0a, 0x12, - 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, - 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, - 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, - 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xea, 0x01, 0x0a, 0x17, 0x46, - 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, - 0x2e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x54, 0x61, - 0x72, 0x67, 0x65, 0x74, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x24, 0x0a, - 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4b, - 0x65, 0x79, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x6f, 0x6f, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, - 0x6f, 0x6f, 0x74, 0x12, 0x34, 0x0a, 0x15, 0x72, 0x65, 0x71, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, - 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x15, 0x72, 0x65, 0x71, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, - 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, - 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, - 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x65, - 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, - 0x50, 0x75, 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x11, 0x0a, 0x0f, - 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, - 0xae, 0x03, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x0b, 0x47, - 0x65, 0x74, 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, - 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x22, 0x00, 0x12, 0x48, 0x0a, 0x0c, 0x50, 0x75, 0x73, 0x68, 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, - 0x70, 0x68, 0x12, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75, 0x73, 0x68, - 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x19, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x44, 0x6f, 0x63, - 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x06, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, - 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, - 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x12, 0x39, 0x0a, 0x07, 0x50, 0x75, 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x12, - 0x16, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x4c, 0x6f, 0x67, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, - 0x2e, 0x50, 0x75, 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, - 0x5b, 0x0a, 0x13, 0x54, 0x72, 0x79, 0x47, 0x65, 0x6e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, - 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, - 0x70, 0x62, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0a, - 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x2e, 0x6e, 0x65, 0x74, - 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x47, - 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, - 0x42, 0x0a, 0x5a, 0x08, 0x2f, 0x3b, 0x6e, 0x65, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x14, 0x0a, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, + 0x6b, 0x65, 0x79, 0x49, 0x44, 0x22, 0xc0, 0x01, 0x0a, 0x19, 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x35, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x45, 0x6e, + 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, + 0x74, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x63, + 0x68, 0x65, 0x6d, 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, + 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x2e, 0x0a, 0x12, 0x65, 0x70, + 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x12, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, + 0x6c, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, + 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, + 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0xea, 0x01, 0x0a, 0x17, 0x46, 0x65, 0x74, + 0x63, 0x68, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x52, + 0x65, 0x70, 0x6c, 0x79, 0x12, 0x35, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x45, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x54, 0x61, 0x72, 0x67, + 0x65, 0x74, 0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x65, + 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0c, 0x52, 0x0d, 0x65, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x65, 0x64, 0x4b, 0x65, 0x79, + 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x6f, 0x6f, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x6f, 0x6f, + 0x74, 0x12, 0x34, 0x0a, 0x15, 0x72, 0x65, 0x71, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, + 0x6c, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, + 0x52, 0x15, 0x72, 0x65, 0x71, 0x45, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, + 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, + 0x61, 0x74, 0x75, 0x72, 0x65, 0x22, 0x13, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, + 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x0e, 0x0a, 0x0c, 0x50, 0x75, + 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x11, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x48, 0x65, 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0xae, 0x03, + 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x45, 0x0a, 0x0b, 0x47, 0x65, 0x74, + 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x12, 0x1a, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, + 0x62, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, + 0x74, 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, + 0x12, 0x48, 0x0a, 0x0c, 0x50, 0x75, 0x73, 0x68, 0x44, 0x6f, 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, + 0x12, 0x1b, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x44, 0x6f, + 0x63, 0x47, 0x72, 0x61, 0x70, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x44, 0x6f, 0x63, 0x47, 0x72, + 0x61, 0x70, 0x68, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x36, 0x0a, 0x06, 0x47, 0x65, + 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x15, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, + 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x6e, 0x65, + 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x12, 0x39, 0x0a, 0x07, 0x50, 0x75, 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x12, 0x16, 0x2e, + 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x50, + 0x75, 0x73, 0x68, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x5b, 0x0a, + 0x13, 0x54, 0x72, 0x79, 0x47, 0x65, 0x6e, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, + 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x46, 0x65, + 0x74, 0x63, 0x68, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, + 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x4b, 0x65, 0x79, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x12, 0x42, 0x0a, 0x0a, 0x47, 0x65, + 0x74, 0x48, 0x65, 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x12, 0x19, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, + 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, + 0x48, 0x65, 0x61, 0x64, 0x4c, 0x6f, 0x67, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x0a, + 0x5a, 0x08, 0x2f, 0x3b, 0x6e, 0x65, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/net/pb/net.proto b/net/pb/net.proto index 6632077c7b..4bde3a4b88 100644 --- a/net/pb/net.proto +++ b/net/pb/net.proto @@ -45,8 +45,8 @@ message EncryptionKeyTarget { // fieldName if the name of the document field that the key is being requested for. // If the fieldName is empty, the key is being requested for the whole document. string fieldName = 2; - // height is the height of the block the encryption key is being requested for. - uint64 height = 3; + // keyID is the hash (Cid) of the key that is being requested. + bytes keyID = 3; } // FetchEncryptionKeyRequest is a request to receive a doc encryption key diff --git a/net/pb/net_vtproto.pb.go b/net/pb/net_vtproto.pb.go index f7c53f117b..4212fb1e3a 100644 --- a/net/pb/net_vtproto.pb.go +++ b/net/pb/net_vtproto.pb.go @@ -400,10 +400,12 @@ func (m *EncryptionKeyTarget) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } - if m.Height != 0 { - i = protohelpers.EncodeVarint(dAtA, i, uint64(m.Height)) + if len(m.KeyID) > 0 { + i -= len(m.KeyID) + copy(dAtA[i:], m.KeyID) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.KeyID))) i-- - dAtA[i] = 0x18 + dAtA[i] = 0x1a } if len(m.FieldName) > 0 { i -= len(m.FieldName) @@ -792,8 +794,9 @@ func (m *EncryptionKeyTarget) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } - if m.Height != 0 { - n += 1 + protohelpers.SizeOfVarint(uint64(m.Height)) + l = len(m.KeyID) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } n += len(m.unknownFields) return n @@ -1684,10 +1687,10 @@ func (m *EncryptionKeyTarget) UnmarshalVT(dAtA []byte) error { m.FieldName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field KeyID", wireType) } - m.Height = 0 + var byteLen int for shift := uint(0); ; shift += 7 { if shift >= 64 { return protohelpers.ErrIntOverflow @@ -1697,11 +1700,26 @@ func (m *EncryptionKeyTarget) UnmarshalVT(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Height |= uint64(b&0x7F) << shift + byteLen |= int(b&0x7F) << shift if b < 0x80 { break } } + if byteLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.KeyID = append(m.KeyID[:0], dAtA[iNdEx:postIndex]...) + if m.KeyID == nil { + m.KeyID = []byte{} + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/net/server_encryption_key.go b/net/server_encryption_key.go index e96d3b9688..dfea15d18e 100644 --- a/net/server_encryption_key.go +++ b/net/server_encryption_key.go @@ -14,9 +14,10 @@ import ( "bytes" "context" "crypto/sha256" - "encoding/binary" "fmt" + "encoding/base64" + "github.com/libp2p/go-libp2p/core/peer" libpeer "github.com/libp2p/go-libp2p/core/peer" rpc "github.com/sourcenetwork/go-libp2p-pubsub-rpc" @@ -42,7 +43,7 @@ func (s *server) getEncryptionKeys( ctx context.Context, req *pb.FetchEncryptionKeyRequest, ) ([]byte, []*pb.EncryptionKeyTarget, error) { - encryptionKeys := make([]byte, 0) + encryptionKeys := make([]byte, 0, len(req.Targets)) targets := make([]*pb.EncryptionKeyTarget, 0, len(req.Targets)) for _, target := range req.Targets { docID, err := client.NewDocIDFromString(string(target.DocID)) @@ -56,7 +57,7 @@ func (s *server) getEncryptionKeys( } encKey, err := encryption.GetKey( encryption.ContextWithStore(ctx, s.peer.encstore), - core.NewEncStoreDocKey(docID.String(), optFieldName, target.Height), + core.NewEncStoreDocKey(docID.String(), optFieldName, string(target.KeyID)), ) if err != nil { return nil, nil, err @@ -133,9 +134,7 @@ func hashFetchEncryptionKeyReply(res *pb.FetchEncryptionKeyReply) []byte { for _, target := range res.Targets { hash.Write(target.DocID) hash.Write([]byte(target.FieldName)) - heightBytes := make([]byte, 8) - binary.BigEndian.PutUint64(heightBytes, target.Height) - hash.Write(heightBytes) + hash.Write([]byte(target.KeyID)) } return hash.Sum(nil) } @@ -199,8 +198,8 @@ func (s *server) prepareFetchEncryptionKeyRequest( for _, encStoreKey := range evt.Keys { encKey := &pb.EncryptionKeyTarget{ - DocID: []byte(encStoreKey.DocID), - Height: encStoreKey.BlockHeight, + DocID: []byte(encStoreKey.DocID), + KeyID: []byte(encStoreKey.KeyID), } if encStoreKey.FieldName.HasValue() { encKey.FieldName = encStoreKey.FieldName.Value() @@ -264,9 +263,7 @@ func hashFetchEncryptionKeyRequest(req *pb.FetchEncryptionKeyRequest) []byte { for _, target := range req.Targets { hash.Write(target.DocID) hash.Write([]byte(target.FieldName)) - heightBytes := make([]byte, 8) - binary.BigEndian.PutUint64(heightBytes, target.Height) - hash.Write(heightBytes) + hash.Write([]byte(target.KeyID)) } return hash.Sum(nil) } @@ -327,19 +324,25 @@ func (s *server) handleFetchEncryptionKeyResponse(resp rpc.Response, req *pb.Fet encKey := decryptedData[:crypto.AESKeySize] decryptedData = decryptedData[crypto.AESKeySize:] - eventData[core.NewEncStoreDocKey(string(target.DocID), optFieldName, target.Height)] = encKey + eventData[core.NewEncStoreDocKey(string(target.DocID), optFieldName, string(target.KeyID))] = encKey } s.peer.bus.Publish(encryption.NewKeysRetrievedMessage(string(req.SchemaRoot), eventData)) } +func encodeToBase64(data []byte) []byte { + encoded := make([]byte, base64.StdEncoding.EncodedLen(len(data))) + base64.StdEncoding.Encode(encoded, data) + return encoded +} + // makeAssociatedData creates the associated data for the encryption key request func makeAssociatedData(req *pb.FetchEncryptionKeyRequest, peerID libpeer.ID) []byte { - return bytes.Join([][]byte{ + return encodeToBase64(bytes.Join([][]byte{ []byte(req.SchemaRoot), []byte(req.EphemeralPublicKey), []byte(peerID), - }, []byte{}) + }, []byte{})) } func (s *server) verifyResponseSignature(res *pb.FetchEncryptionKeyReply, fromPeer peer.ID) (bool, error) { diff --git a/tests/integration/encryption/commit_test.go b/tests/integration/encryption/commit_test.go index 12a363c8b5..4d1efbc2fa 100644 --- a/tests/integration/encryption/commit_test.go +++ b/tests/integration/encryption/commit_test.go @@ -48,7 +48,7 @@ func TestDocEncryption_WithEncryptionOnLWWCRDT_ShouldStoreCommitsDeltaEncrypted( Results: map[string]any{ "commits": []map[string]any{ { - "cid": "bafyreiapgmaxzdyvhmpoh3oibsem5xsl2hpl7wtfalzfv37ikcfjsmt5d4", + "cid": "bafyreibsfegxzo5isgcmwfhw4jpj4eo3atmykyrnnh3a52afxookdrrylu", "collectionID": int64(1), "delta": encrypt(testUtils.CBORValue(21), john21DocID, ""), "docID": john21DocID, @@ -58,7 +58,7 @@ func TestDocEncryption_WithEncryptionOnLWWCRDT_ShouldStoreCommitsDeltaEncrypted( "links": []map[string]any{}, }, { - "cid": "bafyreid7f742ai5fyhvofwn5ortiqcx3jtwahkxcvfb5kspufndfq6iqeu", + "cid": "bafyreifly6elh3267k6kbnbvsalu7suou7dbgkqm3dbpt5w7hnwqkdhnli", "collectionID": int64(1), "delta": encrypt(testUtils.CBORValue("John"), john21DocID, ""), "docID": john21DocID, @@ -68,7 +68,7 @@ func TestDocEncryption_WithEncryptionOnLWWCRDT_ShouldStoreCommitsDeltaEncrypted( "links": []map[string]any{}, }, { - "cid": "bafyreibdoxaj6yeybokybtjl3mprzryjzufoxzhiumibuvtvpsylf5hufi", + "cid": "bafyreiey25ljav736mpbs6ghkmvwstxil4lt4jrte33p6jnixg4nvzz264", "collectionID": int64(1), "delta": nil, "docID": john21DocID, @@ -77,11 +77,11 @@ func TestDocEncryption_WithEncryptionOnLWWCRDT_ShouldStoreCommitsDeltaEncrypted( "height": int64(1), "links": []map[string]any{ { - "cid": "bafyreiapgmaxzdyvhmpoh3oibsem5xsl2hpl7wtfalzfv37ikcfjsmt5d4", + "cid": "bafyreibsfegxzo5isgcmwfhw4jpj4eo3atmykyrnnh3a52afxookdrrylu", "name": "age", }, { - "cid": "bafyreid7f742ai5fyhvofwn5ortiqcx3jtwahkxcvfb5kspufndfq6iqeu", + "cid": "bafyreifly6elh3267k6kbnbvsalu7suou7dbgkqm3dbpt5w7hnwqkdhnli", "name": "name", }, }, diff --git a/tests/integration/encryption/peer_test.go b/tests/integration/encryption/peer_test.go index e88765addf..7b33998e80 100644 --- a/tests/integration/encryption/peer_test.go +++ b/tests/integration/encryption/peer_test.go @@ -60,7 +60,7 @@ func TestDocEncryptionPeer_UponSync_ShouldSyncEncryptedDAG(t *testing.T) { Results: map[string]any{ "commits": []map[string]any{ { - "cid": "bafyreiapgmaxzdyvhmpoh3oibsem5xsl2hpl7wtfalzfv37ikcfjsmt5d4", + "cid": "bafyreibsfegxzo5isgcmwfhw4jpj4eo3atmykyrnnh3a52afxookdrrylu", "collectionID": int64(1), "delta": encrypt(testUtils.CBORValue(21), john21DocID, ""), "docID": john21DocID, @@ -70,7 +70,7 @@ func TestDocEncryptionPeer_UponSync_ShouldSyncEncryptedDAG(t *testing.T) { "links": []map[string]any{}, }, { - "cid": "bafyreid7f742ai5fyhvofwn5ortiqcx3jtwahkxcvfb5kspufndfq6iqeu", + "cid": "bafyreifly6elh3267k6kbnbvsalu7suou7dbgkqm3dbpt5w7hnwqkdhnli", "collectionID": int64(1), "delta": encrypt(testUtils.CBORValue("John"), john21DocID, ""), "docID": john21DocID, @@ -80,7 +80,7 @@ func TestDocEncryptionPeer_UponSync_ShouldSyncEncryptedDAG(t *testing.T) { "links": []map[string]any{}, }, { - "cid": "bafyreibdoxaj6yeybokybtjl3mprzryjzufoxzhiumibuvtvpsylf5hufi", + "cid": "bafyreiey25ljav736mpbs6ghkmvwstxil4lt4jrte33p6jnixg4nvzz264", "collectionID": int64(1), "delta": nil, "docID": john21DocID, @@ -89,11 +89,11 @@ func TestDocEncryptionPeer_UponSync_ShouldSyncEncryptedDAG(t *testing.T) { "height": int64(1), "links": []map[string]any{ { - "cid": "bafyreiapgmaxzdyvhmpoh3oibsem5xsl2hpl7wtfalzfv37ikcfjsmt5d4", + "cid": "bafyreibsfegxzo5isgcmwfhw4jpj4eo3atmykyrnnh3a52afxookdrrylu", "name": "age", }, { - "cid": "bafyreid7f742ai5fyhvofwn5ortiqcx3jtwahkxcvfb5kspufndfq6iqeu", + "cid": "bafyreifly6elh3267k6kbnbvsalu7suou7dbgkqm3dbpt5w7hnwqkdhnli", "name": "name", }, },