From 8ccb7f192491c55f45b09b59e897aa4f8a41e86e Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Mon, 18 Nov 2024 09:56:43 +0100 Subject: [PATCH] fix(protocol): allow encrypted-to-encrypted connections again Encrypted-to-encrypted connections (i.e., ones where both sides set a password) used to work but were broken in the 1.28.0 release. The culprit is the 5342bec1b refactor which slightly changed how the request was constructed, resulting in a bad block hash field. Co-authored-by: Simon Frei --- lib/protocol/encryption.go | 48 ++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/lib/protocol/encryption.go b/lib/protocol/encryption.go index e8d47aec7..6216324be 100644 --- a/lib/protocol/encryption.go +++ b/lib/protocol/encryption.go @@ -212,38 +212,44 @@ func (e encryptedConnection) Request(ctx context.Context, req *Request) ([]byte, if !ok { return e.conn.Request(ctx, req) } + fileKey := e.keyGen.FileKey(req.Name, folderKey) // Encrypt / adjust the request parameters. - origSize := req.Size - origName := req.Name - if req.Size < minPaddedSize { + encSize := req.Size + if encSize < minPaddedSize { // Make a request for minPaddedSize data instead of the smaller // block. We'll chop of the extra data later. - req.Size = minPaddedSize + encSize = minPaddedSize } + encSize += blockOverhead encName := encryptName(req.Name, folderKey) encOffset := req.Offset + int64(req.BlockNo*blockOverhead) - encSize := req.Size + blockOverhead + encHash := encryptBlockHash(req.Hash, req.Offset, fileKey) // Perform that request, getting back an encrypted block. - req.Name = encName - req.Offset = encOffset - req.Size = encSize - bs, err := e.conn.Request(ctx, req) + encReq := &Request{ + ID: req.ID, + Folder: req.Folder, + Name: encName, + Offset: encOffset, + Size: encSize, + Hash: encHash, + BlockNo: req.BlockNo, + } + bs, err := e.conn.Request(ctx, encReq) if err != nil { return nil, err } // Return the decrypted block (or an error if it fails decryption) - fileKey := e.keyGen.FileKey(origName, folderKey) bs, err = DecryptBytes(bs, fileKey) if err != nil { return nil, err } - return bs[:origSize], nil + return bs[:req.Size], nil } func (e encryptedConnection) DownloadProgress(ctx context.Context, dp *DownloadProgress) { @@ -327,15 +333,7 @@ func encryptFileInfo(keyGen *KeyGenerator, fi FileInfo, folderKey *[keySize]byte b.Size = minPaddedSize } size := b.Size + blockOverhead - - // The offset goes into the encrypted block hash as additional data, - // essentially mixing in with the nonce. This means a block hash - // remains stable for the same data at the same offset, but doesn't - // reveal the existence of identical data blocks at other offsets. - var additional [8]byte - binary.BigEndian.PutUint64(additional[:], uint64(b.Offset)) - hash := encryptDeterministic(b.Hash, fileKey, additional[:]) - + hash := encryptBlockHash(b.Hash, b.Offset, fileKey) blocks[i] = BlockInfo{ Hash: hash, Offset: offset, @@ -374,6 +372,16 @@ func encryptFileInfo(keyGen *KeyGenerator, fi FileInfo, folderKey *[keySize]byte return enc } +func encryptBlockHash(hash []byte, offset int64, fileKey *[keySize]byte) []byte { + // The offset goes into the encrypted block hash as additional data, + // essentially mixing in with the nonce. This means a block hash + // remains stable for the same data at the same offset, but doesn't + // reveal the existence of identical data blocks at other offsets. + var additional [8]byte + binary.BigEndian.PutUint64(additional[:], uint64(offset)) + return encryptDeterministic(hash, fileKey, additional[:]) +} + func decryptFileInfos(keyGen *KeyGenerator, files []FileInfo, folderKey *[keySize]byte) error { for i, fi := range files { decFI, err := DecryptFileInfo(keyGen, fi, folderKey)