aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/configfile/config_file.go4
-rw-r--r--internal/contentenc/content.go16
-rw-r--r--internal/contentenc/content_test.go12
-rw-r--r--internal/cryptocore/cryptocore.go4
-rw-r--r--internal/cryptocore/cryptocore_test.go8
-rw-r--r--internal/fusefrontend/args.go2
-rw-r--r--internal/fusefrontend/file.go4
-rw-r--r--internal/fusefrontend/fs.go4
-rw-r--r--internal/fusefrontend_reverse/rfs.go4
-rw-r--r--internal/speed/speed.go2
-rw-r--r--internal/stupidgcm/stupidgcm.go18
-rw-r--r--internal/stupidgcm/stupidgcm_test.go4
-rw-r--r--internal/stupidgcm/without_openssl.go5
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{}