decryptFile reads, decrypts and verifies all the blocks in src, writing it to dst if dst is non-nil. (If dst is nil it just becomes a read-and-verify operation.)
(encFi *protocol.FileInfo, plainFi *protocol.FileInfo, src io.ReaderAt, dst io.WriterAt)
| 209 | // it to dst if dst is non-nil. (If dst is nil it just becomes a |
| 210 | // read-and-verify operation.) |
| 211 | func (c *CLI) decryptFile(encFi *protocol.FileInfo, plainFi *protocol.FileInfo, src io.ReaderAt, dst io.WriterAt) error { |
| 212 | // The encrypted and plaintext files must consist of an equal number of blocks |
| 213 | if len(encFi.Blocks) != len(plainFi.Blocks) { |
| 214 | return fmt.Errorf("block count mismatch: encrypted %d != plaintext %d", len(encFi.Blocks), len(plainFi.Blocks)) |
| 215 | } |
| 216 | |
| 217 | fileKey := c.keyGen.FileKey(plainFi.Name, c.folderKey) |
| 218 | for i, encBlock := range encFi.Blocks { |
| 219 | // Read the encrypted block |
| 220 | buf := make([]byte, encBlock.Size) |
| 221 | if _, err := src.ReadAt(buf, encBlock.Offset); err != nil { |
| 222 | return fmt.Errorf("encrypted block %d (%d bytes): %w", i, encBlock.Size, err) |
| 223 | } |
| 224 | |
| 225 | // Decrypt it |
| 226 | dec, err := protocol.DecryptBytes(buf, fileKey) |
| 227 | if err != nil { |
| 228 | return fmt.Errorf("encrypted block %d (%d bytes): %w", i, encBlock.Size, err) |
| 229 | } |
| 230 | |
| 231 | // Verify the block size against the expected plaintext |
| 232 | plainBlock := plainFi.Blocks[i] |
| 233 | if i == len(plainFi.Blocks)-1 && len(dec) > plainBlock.Size { |
| 234 | // The last block might be padded, which is fine (we skip the padding) |
| 235 | dec = dec[:plainBlock.Size] |
| 236 | } else if len(dec) != plainBlock.Size { |
| 237 | return fmt.Errorf("plaintext block %d size mismatch, actual %d != expected %d", i, len(dec), plainBlock.Size) |
| 238 | } |
| 239 | |
| 240 | // Verify the hash against the plaintext block info |
| 241 | if !scanner.Validate(dec, plainBlock.Hash) { |
| 242 | // The block decrypted correctly but fails the hash check. This |
| 243 | // is odd and unexpected, but it it's still a valid block from |
| 244 | // the source. The file might have changed while we pulled it? |
| 245 | err := fmt.Errorf("plaintext block %d (%d bytes) failed validation after decryption", i, plainBlock.Size) |
| 246 | if c.Continue { |
| 247 | log.Printf("Warning: %s: %s: %v", encFi.Name, plainFi.Name, err) |
| 248 | } else { |
| 249 | return err |
| 250 | } |
| 251 | } |
| 252 | |
| 253 | // Write it to the destination, unless we're just verifying. |
| 254 | if dst != nil { |
| 255 | if _, err := dst.WriteAt(dec, plainBlock.Offset); err != nil { |
| 256 | return err |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | |
| 261 | return nil |
| 262 | } |
| 263 | |
| 264 | // loadEncryptedFileInfo loads the encrypted FileInfo trailer from a file on |
| 265 | // disk. |