diff options
author | danim7 | 2017-04-08 02:09:28 +0200 |
---|---|---|
committer | Jakob Unterwurzacher | 2017-04-23 23:11:56 +0200 |
commit | f1945c4daae65074cfca8f0ab5b97ac5a50c24a0 (patch) | |
tree | f6a555c9d7fedb0da6f5e21981f4154fa413c8c0 /internal | |
parent | 9777e4bf7ea2aa75ab443dc6e15c42103eb6b027 (diff) |
Add -forcedecode
Force decode of encrypted files even if the integrity check fails, instead of
failing with an IO error. Warning messages are still printed to syslog if corrupted
files are encountered.
It can be useful to recover files from disks with bad sectors or other corrupted
media.
Closes https://github.com/rfjakob/gocryptfs/pull/102 .
Diffstat (limited to 'internal')
-rw-r--r-- | internal/configfile/config_file.go | 4 | ||||
-rw-r--r-- | internal/contentenc/content.go | 16 | ||||
-rw-r--r-- | internal/contentenc/content_test.go | 12 | ||||
-rw-r--r-- | internal/cryptocore/cryptocore.go | 4 | ||||
-rw-r--r-- | internal/cryptocore/cryptocore_test.go | 8 | ||||
-rw-r--r-- | internal/fusefrontend/args.go | 2 | ||||
-rw-r--r-- | internal/fusefrontend/file.go | 4 | ||||
-rw-r--r-- | internal/fusefrontend/fs.go | 4 | ||||
-rw-r--r-- | internal/fusefrontend_reverse/rfs.go | 4 | ||||
-rw-r--r-- | internal/speed/speed.go | 2 | ||||
-rw-r--r-- | internal/stupidgcm/stupidgcm.go | 18 | ||||
-rw-r--r-- | internal/stupidgcm/stupidgcm_test.go | 4 | ||||
-rw-r--r-- | internal/stupidgcm/without_openssl.go | 5 |
13 files changed, 57 insertions, 30 deletions
diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index bf56f8b..5605e1d 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -225,7 +225,7 @@ func getKeyEncrypter(scryptHash []byte, useHKDF bool) *contentenc.ContentEnc { if useHKDF { IVLen = contentenc.DefaultIVBits } - cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, IVLen, useHKDF) - ce := contentenc.New(cc, 4096) + cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, IVLen, useHKDF, false) + ce := contentenc.New(cc, 4096, false) return ce } diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index a2a263c..9998c06 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -9,6 +9,7 @@ import ( "log" "github.com/rfjakob/gocryptfs/internal/cryptocore" + "github.com/rfjakob/gocryptfs/internal/stupidgcm" "github.com/rfjakob/gocryptfs/internal/tlog" ) @@ -46,10 +47,12 @@ type ContentEnc struct { allZeroBlock []byte // All-zero block of size IVBitLen/8, for fast compares allZeroNonce []byte + // Force decode even if integrity check fails (openSSL only) + forceDecode bool } // New returns an initialized ContentEnc instance. -func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc { +func New(cc *cryptocore.CryptoCore, plainBS uint64, forceDecode bool) *ContentEnc { cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen return &ContentEnc{ @@ -58,6 +61,7 @@ func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc { cipherBS: cipherBS, allZeroBlock: make([]byte, cipherBS), allZeroNonce: make([]byte, cc.IVLen), + forceDecode: forceDecode, } } @@ -82,7 +86,9 @@ func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, file var pBlock []byte pBlock, err = be.DecryptBlock(cBlock, firstBlockNo, fileID) if err != nil { - break + if be.forceDecode == false || (be.forceDecode == true && stupidgcm.AuthError != err) { + break + } } pBuf.Write(pBlock) firstBlockNo++ @@ -133,7 +139,11 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b if err != nil { tlog.Warn.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig)) tlog.Debug.Println(hex.Dump(ciphertextOrig)) - return nil, err + if be.forceDecode == true { + return plaintext, err + } else { + return nil, err + } } return plaintext, nil diff --git a/internal/contentenc/content_test.go b/internal/contentenc/content_test.go index 8ce496d..e4d4a3e 100644 --- a/internal/contentenc/content_test.go +++ b/internal/contentenc/content_test.go @@ -23,8 +23,8 @@ func TestSplitRange(t *testing.T) { testRange{6654, 8945}) key := make([]byte, cryptocore.KeyLen) - cc := cryptocore.New(key, cryptocore.BackendOpenSSL, DefaultIVBits, true) - f := New(cc, DefaultBS) + cc := cryptocore.New(key, cryptocore.BackendOpenSSL, DefaultIVBits, true, false) + f := New(cc, DefaultBS, false) for _, r := range ranges { parts := f.ExplodePlainRange(r.offset, r.length) @@ -51,8 +51,8 @@ func TestCiphertextRange(t *testing.T) { testRange{6654, 8945}) key := make([]byte, cryptocore.KeyLen) - cc := cryptocore.New(key, cryptocore.BackendOpenSSL, DefaultIVBits, true) - f := New(cc, DefaultBS) + cc := cryptocore.New(key, cryptocore.BackendOpenSSL, DefaultIVBits, true, false) + f := New(cc, DefaultBS, false) for _, r := range ranges { @@ -74,8 +74,8 @@ func TestCiphertextRange(t *testing.T) { func TestBlockNo(t *testing.T) { key := make([]byte, cryptocore.KeyLen) - cc := cryptocore.New(key, cryptocore.BackendOpenSSL, DefaultIVBits, true) - f := New(cc, DefaultBS) + cc := cryptocore.New(key, cryptocore.BackendOpenSSL, DefaultIVBits, true, false) + f := New(cc, DefaultBS, false) b := f.CipherOffToBlockNo(788) if b != 0 { diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index 5244104..2c352c2 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -51,7 +51,7 @@ type CryptoCore struct { // Even though the "GCMIV128" feature flag is now mandatory, we must still // support 96-bit IVs here because they were used for encrypting the master // key in gocryptfs.conf up to gocryptfs v1.2. v1.3 switched to 128 bits. -func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoCore { +func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDecode bool) *CryptoCore { if len(key) != KeyLen { log.Panic(fmt.Sprintf("Unsupported key length %d", len(key))) } @@ -86,7 +86,7 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoC if IVLen != 16 { log.Panic("stupidgcm only supports 128-bit IVs") } - aeadCipher = stupidgcm.New(gcmKey) + aeadCipher = stupidgcm.New(gcmKey, forceDecode) case BackendGoGCM: goGcmBlockCipher, err := aes.NewCipher(gcmKey) if err != nil { diff --git a/internal/cryptocore/cryptocore_test.go b/internal/cryptocore/cryptocore_test.go index 25f6572..4c34652 100644 --- a/internal/cryptocore/cryptocore_test.go +++ b/internal/cryptocore/cryptocore_test.go @@ -8,15 +8,15 @@ import ( func TestCryptoCoreNew(t *testing.T) { key := make([]byte, 32) for _, useHKDF := range []bool{true, false} { - c := New(key, BackendOpenSSL, 128, useHKDF) + c := New(key, BackendOpenSSL, 128, useHKDF, false) if c.IVLen != 16 { t.Fail() } - c = New(key, BackendGoGCM, 96, useHKDF) + c = New(key, BackendGoGCM, 96, useHKDF, false) if c.IVLen != 12 { t.Fail() } - c = New(key, BackendGoGCM, 128, useHKDF) + c = New(key, BackendGoGCM, 128, useHKDF, false) if c.IVLen != 16 { t.Fail() } @@ -32,5 +32,5 @@ func TestNewPanic(t *testing.T) { }() key := make([]byte, 16) - New(key, BackendOpenSSL, 128, true) + New(key, BackendOpenSSL, 128, true, false) } diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go index ddfb9dc..5781db8 100644 --- a/internal/fusefrontend/args.go +++ b/internal/fusefrontend/args.go @@ -31,4 +31,6 @@ type Args struct { HKDF bool // Try to serialize read operations, "-serialize_reads" SerializeReads bool + // Force decode even if integrity check fails (openSSL only) + ForceDecode bool } diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index 84ce058..4d75d64 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -202,7 +202,9 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) { if err != nil { curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext))) tlog.Warn.Printf("ino%d: doRead: corrupt block #%d: %v", f.devIno.ino, curruptBlockNo, err) - return nil, fuse.EIO + if (f.fs.args.ForceDecode == false) { + return nil, fuse.EIO + } } // Crop down to the relevant part diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index 28c43b6..4aa4ffd 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -40,8 +40,8 @@ var _ pathfs.FileSystem = &FS{} // Verify that interface is implemented. // NewFS returns a new encrypted FUSE overlay filesystem. func NewFS(args Args) *FS { - cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits, args.HKDF) - contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) + cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits, args.HKDF, args.ForceDecode) + contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS, args.ForceDecode) nameTransform := nametransform.New(cryptoCore.EMECipher, args.LongNames, args.Raw64) if args.SerializeReads { diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index a94f448..3c52244 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -44,8 +44,8 @@ func NewFS(args fusefrontend.Args) *ReverseFS { log.Panic("reverse mode must use AES-SIV, everything else is insecure") } initLongnameCache() - cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits, args.HKDF) - contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS) + cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.DefaultIVBits, args.HKDF, false) + contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS, false) nameTransform := nametransform.New(cryptoCore.EMECipher, args.LongNames, args.Raw64) return &ReverseFS{ diff --git a/internal/speed/speed.go b/internal/speed/speed.go index f9bf93c..8732576 100644 --- a/internal/speed/speed.go +++ b/internal/speed/speed.go @@ -72,7 +72,7 @@ func bStupidGCM(b *testing.B) { in := make([]byte, blockSize) b.SetBytes(int64(len(in))) - sGCM := stupidgcm.New(key) + sGCM := stupidgcm.New(key, false) b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/internal/stupidgcm/stupidgcm.go b/internal/stupidgcm/stupidgcm.go index a1a5a14..133ee1a 100644 --- a/internal/stupidgcm/stupidgcm.go +++ b/internal/stupidgcm/stupidgcm.go @@ -26,17 +26,21 @@ const ( // stupidGCM implements the cipher.AEAD interface type stupidGCM struct { - key []byte + key []byte + forceDecode bool } +//authentication error +var AuthError error = fmt.Errorf("stupidgcm: message authentication failed") + var _ cipher.AEAD = &stupidGCM{} // New returns a new cipher.AEAD implementation.. -func New(key []byte) cipher.AEAD { +func New(key []byte, forceDecode bool) cipher.AEAD { if len(key) != keyLen { log.Panicf("Only %d-byte keys are supported", keyLen) } - return stupidGCM{key: key} + return stupidGCM{key: key, forceDecode: forceDecode} } func (g stupidGCM) NonceSize() int { @@ -186,7 +190,13 @@ func (g stupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) { C.EVP_CIPHER_CTX_free(ctx) if res != 1 { - return nil, fmt.Errorf("stupidgcm: message authentication failed") + // The error code must always be checked by the calling function, because the decrypted buffer + // may contain corrupted data that we are returning in case the user forced reads + if g.forceDecode == true { + return append(dst, buf...), AuthError + } else { + return nil, AuthError + } } return append(dst, buf...), nil diff --git a/internal/stupidgcm/stupidgcm_test.go b/internal/stupidgcm/stupidgcm_test.go index 3081085..eb322f2 100644 --- a/internal/stupidgcm/stupidgcm_test.go +++ b/internal/stupidgcm/stupidgcm_test.go @@ -27,7 +27,7 @@ func randBytes(n int) []byte { // GCM implemenatation and verifies that the results are identical. func TestEncryptDecrypt(t *testing.T) { key := randBytes(32) - sGCM := New(key) + sGCM := New(key, false) authData := randBytes(24) iv := randBytes(16) dst := make([]byte, 71) // 71 = random length @@ -77,7 +77,7 @@ func TestEncryptDecrypt(t *testing.T) { // error func TestCorruption(t *testing.T) { key := randBytes(32) - sGCM := New(key) + sGCM := New(key, false) authData := randBytes(24) iv := randBytes(16) diff --git a/internal/stupidgcm/without_openssl.go b/internal/stupidgcm/without_openssl.go index 18c5ddc..52d8fa0 100644 --- a/internal/stupidgcm/without_openssl.go +++ b/internal/stupidgcm/without_openssl.go @@ -14,12 +14,15 @@ const ( BuiltWithoutOpenssl = true ) +//authentication error - needed to compile as same varaible is exported when openssl is enable via stupidgcm.go +var AuthError error = fmt.Errorf("stupidgcm: message authentication failed with openssl disabled!") + func errExit() { fmt.Fprintln(os.Stderr, "gocryptfs has been compiled without openssl support but you are still trying to use openssl") os.Exit(2) } -func New(_ []byte) stupidGCM { +func New(_ []byte, _ bool) stupidGCM { errExit() // Never reached return stupidGCM{} |