Repository: syncthing/syncthing
File: cmd/syncthing/decrypt/decrypt.go (Go)

Code before:
			// The last block might be padded, which is fine (we skip the padding)
			dec = dec[:plainBlock.Size]
		} else if len(dec) != plainBlock.Size {
			return fmt.Errorf("plaintext block %d size mismatch, actual %d != expected %d", i, len(dec), plainBlock.Size)
		}

		// Verify the hash against the plaintext block info
		if !scanner.Validate(dec, plainBlock.Hash, 0) {
			// The block decrypted correctly but fails the hash check. This
			// is odd and unexpected, but it it's still a valid block from
			// the source. The file might have changed while we pulled it?
			err := fmt.Errorf("plaintext block %d (%d bytes) failed validation after decryption", i, plainBlock.Size)
			if c.Continue {
				log.Printf("Warning: %s: %v", plainFi.Name, err)
			} else {
				return err
			}
		}

		// Write it to the destination, unless we're just verifying.
		if dst != nil {
			if _, err := dst.WriteAt(dec, plainBlock.Offset); err != nil {
				return err
			}
		}
	}

	return nil
}

// loadEncryptedFileInfo loads the encrypted FileInfo trailer from a file on
// disk.
func (c *CLI) loadEncryptedFileInfo(fd fs.File) (*protocol.FileInfo, error) {
	// Seek to the size of the trailer block
	if _, err := fd.Seek(-4, io.SeekEnd); err != nil {
		return nil, err
	}
	var bs [4]byte
	if _, err := io.ReadFull(fd, bs[:]); err != nil {
		return nil, err
	}
	size := int64(binary.BigEndian.Uint32(bs[:]))

	// Seek to the start of the trailer
	if _, err := fd.Seek(-(4 + size), io.SeekEnd); err != nil {
		return nil, err
	}
	trailer := make([]byte, size)
	if _, err := io.ReadFull(fd, trailer); err != nil {
		return nil, err
	}

Review comment (suggestion):
Maybe a relict from before the `withContinue` wrapper existed? Or I don't see the relevant difference of this error to the other encryption related errors in this function.

Code after:
		// Decrypt it
		dec, err := protocol.DecryptBytes(buf, fileKey)
		if err != nil {
			return fmt.Errorf("encrypted block %d (%d bytes): %w", i, encBlock.Size, err)
		}

		// Verify the block size against the expected plaintext
		plainBlock := plainFi.Blocks[i]
		if i == len(plainFi.Blocks)-1 && len(dec) > plainBlock.Size {
			// The last block might be padded, which is fine (we skip the padding)
			dec = dec[:plainBlock.Size]
		} else if len(dec) != plainBlock.Size {
			return fmt.Errorf("plaintext block %d size mismatch, actual %d != expected %d", i, len(dec), plainBlock.Size)
		}

		// Verify the hash against the plaintext block info
		if !scanner.Validate(dec, plainBlock.Hash, 0) {
			// The block decrypted correctly but fails the hash check. This
			// is odd and unexpected, but it it's still a valid block from
			// the source. The file might have changed while we pulled it?
			err := fmt.Errorf("plaintext block %d (%d bytes) failed validation after decryption", i, plainBlock.Size)
			if c.Continue {
				log.Printf("Warning: %s: %s: %v", encFi.Name, plainFi.Name, err)
			} else {
				return err
			}
		}

		// Write it to the destination, unless we're just verifying.
		if dst != nil {
			if _, err := dst.WriteAt(dec, plainBlock.Offset); err != nil {
				return err
			}
		}
	}

	return nil
}

// loadEncryptedFileInfo loads the encrypted FileInfo trailer from a file on
// disk.
func (c *CLI) loadEncryptedFileInfo(fd fs.File) (*protocol.FileInfo, error) {
	// Seek to the size of the trailer block
	if _, err := fd.Seek(-4, io.SeekEnd); err != nil {
		return nil, err
	}
	var bs [4]byte
	if _, err := io.ReadFull(fd, bs[:]); err != nil {
		return nil, err
	}
	size := int64(binary.BigEndian.Uint32(bs[:]))