diff options
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{} |