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 <freisim93@gmail.com>
This commit is contained in:
Jakob Borg 2024-11-18 09:56:43 +01:00
parent 65d0ca8aa9
commit 8ccb7f1924
No known key found for this signature in database

View File

@ -212,38 +212,44 @@ func (e encryptedConnection) Request(ctx context.Context, req *Request) ([]byte,
if !ok { if !ok {
return e.conn.Request(ctx, req) return e.conn.Request(ctx, req)
} }
fileKey := e.keyGen.FileKey(req.Name, folderKey)
// Encrypt / adjust the request parameters. // Encrypt / adjust the request parameters.
origSize := req.Size encSize := req.Size
origName := req.Name if encSize < minPaddedSize {
if req.Size < minPaddedSize {
// Make a request for minPaddedSize data instead of the smaller // Make a request for minPaddedSize data instead of the smaller
// block. We'll chop of the extra data later. // block. We'll chop of the extra data later.
req.Size = minPaddedSize encSize = minPaddedSize
} }
encSize += blockOverhead
encName := encryptName(req.Name, folderKey) encName := encryptName(req.Name, folderKey)
encOffset := req.Offset + int64(req.BlockNo*blockOverhead) 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. // Perform that request, getting back an encrypted block.
req.Name = encName encReq := &Request{
req.Offset = encOffset ID: req.ID,
req.Size = encSize Folder: req.Folder,
bs, err := e.conn.Request(ctx, req) Name: encName,
Offset: encOffset,
Size: encSize,
Hash: encHash,
BlockNo: req.BlockNo,
}
bs, err := e.conn.Request(ctx, encReq)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Return the decrypted block (or an error if it fails decryption) // Return the decrypted block (or an error if it fails decryption)
fileKey := e.keyGen.FileKey(origName, folderKey)
bs, err = DecryptBytes(bs, fileKey) bs, err = DecryptBytes(bs, fileKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bs[:origSize], nil return bs[:req.Size], nil
} }
func (e encryptedConnection) DownloadProgress(ctx context.Context, dp *DownloadProgress) { 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 b.Size = minPaddedSize
} }
size := b.Size + blockOverhead size := b.Size + blockOverhead
hash := encryptBlockHash(b.Hash, b.Offset, fileKey)
// 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[:])
blocks[i] = BlockInfo{ blocks[i] = BlockInfo{
Hash: hash, Hash: hash,
Offset: offset, Offset: offset,
@ -374,6 +372,16 @@ func encryptFileInfo(keyGen *KeyGenerator, fi FileInfo, folderKey *[keySize]byte
return enc 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 { func decryptFileInfos(keyGen *KeyGenerator, files []FileInfo, folderKey *[keySize]byte) error {
for i, fi := range files { for i, fi := range files {
decFI, err := DecryptFileInfo(keyGen, fi, folderKey) decFI, err := DecryptFileInfo(keyGen, fi, folderKey)