diff --git a/backend/crypt/cipher.go b/backend/crypt/cipher.go index e90d75615..017eabea8 100644 --- a/backend/crypt/cipher.go +++ b/backend/crypt/cipher.go @@ -179,6 +179,7 @@ type Cipher struct { buffers sync.Pool // encrypt/decrypt buffers cryptoRand io.Reader // read crypto random numbers from here dirNameEncrypt bool + passBadBlocks bool // if set passed bad blocks as zeroed blocks } // newCipher initialises the cipher. If salt is "" then it uses a built in salt val @@ -199,6 +200,11 @@ func newCipher(mode NameEncryptionMode, password, salt string, dirNameEncrypt bo return c, nil } +// Call to set bad block pass through +func (c *Cipher) setPassBadBlocks(passBadBlocks bool) { + c.passBadBlocks = passBadBlocks +} + // Key creates all the internal keys from the password passed in using // scrypt. // @@ -864,7 +870,14 @@ func (fh *decrypter) fillBuffer() (err error) { if err != nil && err != io.EOF { return err // return pending error as it is likely more accurate } - return ErrorEncryptedBadBlock + if !fh.c.passBadBlocks { + return ErrorEncryptedBadBlock + } + fs.Errorf(nil, "crypt: ignoring: %v", ErrorEncryptedBadBlock) + // Zero out the bad block and continue + for i := range fh.buf[:n] { + fh.buf[i] = 0 + } } fh.bufIndex = 0 fh.bufSize = n - blockHeaderSize diff --git a/backend/crypt/cipher_test.go b/backend/crypt/cipher_test.go index 6b683b437..c8710605e 100644 --- a/backend/crypt/cipher_test.go +++ b/backend/crypt/cipher_test.go @@ -1536,6 +1536,17 @@ func TestDecrypterRead(t *testing.T) { } file16copy[i] ^= 0xFF } + + // Test that we can corrupt a byte and read zeroes if + // passBadBlocks is set + copy(file16copy, file16) + file16copy[len(file16copy)-1] ^= 0xFF + c.passBadBlocks = true + fh, err = c.newDecrypter(io.NopCloser(bytes.NewBuffer(file16copy))) + assert.NoError(t, err) + buf, err := io.ReadAll(fh) + assert.NoError(t, err) + assert.Equal(t, make([]byte, 16), buf) } func TestDecrypterClose(t *testing.T) { diff --git a/backend/crypt/crypt.go b/backend/crypt/crypt.go index f3eb8317d..22be1bb07 100644 --- a/backend/crypt/crypt.go +++ b/backend/crypt/crypt.go @@ -119,6 +119,15 @@ names, or for debugging purposes.`, Help: "Encrypt file data.", }, }, + }, { + Name: "pass_bad_blocks", + Help: `If set this will pass bad blocks through as all 0. + +This should not be set in normal operation, it should only be set if +trying to recover a crypted file with errors and it is desired to +recover as much of the file as possible.`, + Default: false, + Advanced: true, }, { Name: "filename_encoding", Help: `How to encode the encrypted filename to text string. @@ -174,6 +183,7 @@ func newCipherForConfig(opt *Options) (*Cipher, error) { if err != nil { return nil, fmt.Errorf("failed to make cipher: %w", err) } + cipher.setPassBadBlocks(opt.PassBadBlocks) return cipher, nil } @@ -262,6 +272,7 @@ type Options struct { Password2 string `config:"password2"` ServerSideAcrossConfigs bool `config:"server_side_across_configs"` ShowMapping bool `config:"show_mapping"` + PassBadBlocks bool `config:"pass_bad_blocks"` FilenameEncoding string `config:"filename_encoding"` }