diff options
| author | Jakob Unterwurzacher | 2016-02-06 19:20:54 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2016-02-06 19:22:35 +0100 | 
| commit | 2b8cbd944149afe51fadddbd67ee4499d1d86250 (patch) | |
| tree | 76361984cc4394bbb9b19ae987aeaff71fb6073b | |
| parent | adcfbd79a8b8bb85cbee25996ab622a05de0dbc1 (diff) | |
Major refactoring: Split up "cryptfs" into several internal packages
"git status" for reference:
deleted:    cryptfs/cryptfs.go
deleted:    cryptfs/names_core.go
modified:   integration_tests/cli_test.go
modified:   integration_tests/helpers.go
renamed:    cryptfs/config_file.go -> internal/configfile/config_file.go
renamed:    cryptfs/config_test.go -> internal/configfile/config_test.go
renamed:    cryptfs/config_test/.gitignore -> internal/configfile/config_test/.gitignore
renamed:    cryptfs/config_test/PlaintextNames.conf -> internal/configfile/config_test/PlaintextNames.conf
renamed:    cryptfs/config_test/StrangeFeature.conf -> internal/configfile/config_test/StrangeFeature.conf
renamed:    cryptfs/config_test/v1.conf -> internal/configfile/config_test/v1.conf
renamed:    cryptfs/config_test/v2.conf -> internal/configfile/config_test/v2.conf
renamed:    cryptfs/kdf.go -> internal/configfile/kdf.go
renamed:    cryptfs/kdf_test.go -> internal/configfile/kdf_test.go
renamed:    cryptfs/cryptfs_content.go -> internal/contentenc/content.go
new file:   internal/contentenc/content_api.go
renamed:    cryptfs/content_test.go -> internal/contentenc/content_test.go
renamed:    cryptfs/file_header.go -> internal/contentenc/file_header.go
renamed:    cryptfs/intrablock.go -> internal/contentenc/intrablock.go
renamed:    cryptfs/address_translation.go -> internal/contentenc/offsets.go
new file:   internal/cryptocore/crypto_api.go
renamed:    cryptfs/gcm_go1.4.go -> internal/cryptocore/gcm_go1.4.go
renamed:    cryptfs/gcm_go1.5.go -> internal/cryptocore/gcm_go1.5.go
renamed:    cryptfs/nonce.go -> internal/cryptocore/nonce.go
renamed:    cryptfs/openssl_aead.go -> internal/cryptocore/openssl_aead.go
renamed:    cryptfs/openssl_benchmark.bash -> internal/cryptocore/openssl_benchmark.bash
renamed:    cryptfs/openssl_test.go -> internal/cryptocore/openssl_test.go
new file:   internal/nametransform/name_api.go
new file:   internal/nametransform/names_core.go
renamed:    cryptfs/names_diriv.go -> internal/nametransform/names_diriv.go
renamed:    cryptfs/names_noiv.go -> internal/nametransform/names_noiv.go
renamed:    cryptfs/names_test.go -> internal/nametransform/names_test.go
new file:   internal/nametransform/pad16.go
renamed:    cryptfs/log.go -> internal/toggledlog/log.go
renamed:    cryptfs/log_go1.4.go -> internal/toggledlog/log_go1.4.go
renamed:    cryptfs/log_go1.5.go -> internal/toggledlog/log_go1.5.go
modified:   main.go
modified:   masterkey.go
modified:   pathfs_frontend/file.go
modified:   pathfs_frontend/file_holes.go
modified:   pathfs_frontend/fs.go
modified:   pathfs_frontend/fs_dir.go
modified:   pathfs_frontend/names.go
modified:   test.bash
| -rw-r--r-- | cryptfs/cryptfs.go | 83 | ||||
| -rw-r--r-- | cryptfs/names_core.go | 134 | ||||
| -rw-r--r-- | integration_tests/cli_test.go | 15 | ||||
| -rw-r--r-- | integration_tests/helpers.go | 6 | ||||
| -rw-r--r-- | internal/configfile/config_file.go (renamed from cryptfs/config_file.go) | 29 | ||||
| -rw-r--r-- | internal/configfile/config_test.go (renamed from cryptfs/config_test.go) | 2 | ||||
| -rw-r--r-- | internal/configfile/config_test/.gitignore (renamed from cryptfs/config_test/.gitignore) | 0 | ||||
| -rw-r--r-- | internal/configfile/config_test/PlaintextNames.conf (renamed from cryptfs/config_test/PlaintextNames.conf) | 0 | ||||
| -rw-r--r-- | internal/configfile/config_test/StrangeFeature.conf (renamed from cryptfs/config_test/StrangeFeature.conf) | 0 | ||||
| -rw-r--r-- | internal/configfile/config_test/v1.conf (renamed from cryptfs/config_test/v1.conf) | 0 | ||||
| -rw-r--r-- | internal/configfile/config_test/v2.conf (renamed from cryptfs/config_test/v2.conf) | 0 | ||||
| -rw-r--r-- | internal/configfile/kdf.go (renamed from cryptfs/kdf.go) | 15 | ||||
| -rw-r--r-- | internal/configfile/kdf_test.go (renamed from cryptfs/kdf_test.go) | 2 | ||||
| -rw-r--r-- | internal/contentenc/content.go (renamed from cryptfs/cryptfs_content.go) | 49 | ||||
| -rw-r--r-- | internal/contentenc/content_api.go | 31 | ||||
| -rw-r--r-- | internal/contentenc/content_test.go (renamed from cryptfs/content_test.go) | 2 | ||||
| -rw-r--r-- | internal/contentenc/file_header.go (renamed from cryptfs/file_header.go) | 18 | ||||
| -rw-r--r-- | internal/contentenc/intrablock.go (renamed from cryptfs/intrablock.go) | 4 | ||||
| -rw-r--r-- | internal/contentenc/offsets.go (renamed from cryptfs/address_translation.go) | 30 | ||||
| -rw-r--r-- | internal/cryptocore/crypto_api.go | 56 | ||||
| -rw-r--r-- | internal/cryptocore/gcm_go1.4.go (renamed from cryptfs/gcm_go1.4.go) | 2 | ||||
| -rw-r--r-- | internal/cryptocore/gcm_go1.5.go (renamed from cryptfs/gcm_go1.5.go) | 2 | ||||
| -rw-r--r-- | internal/cryptocore/nonce.go (renamed from cryptfs/nonce.go) | 8 | ||||
| -rw-r--r-- | internal/cryptocore/openssl_aead.go (renamed from cryptfs/openssl_aead.go) | 14 | ||||
| -rwxr-xr-x | internal/cryptocore/openssl_benchmark.bash (renamed from cryptfs/openssl_benchmark.bash) | 0 | ||||
| -rw-r--r-- | internal/cryptocore/openssl_test.go (renamed from cryptfs/openssl_test.go) | 2 | ||||
| -rw-r--r-- | internal/nametransform/name_api.go | 16 | ||||
| -rw-r--r-- | internal/nametransform/names_core.go | 63 | ||||
| -rw-r--r-- | internal/nametransform/names_diriv.go (renamed from cryptfs/names_diriv.go) | 41 | ||||
| -rw-r--r-- | internal/nametransform/names_noiv.go (renamed from cryptfs/names_noiv.go) | 14 | ||||
| -rw-r--r-- | internal/nametransform/names_test.go (renamed from cryptfs/names_test.go) | 2 | ||||
| -rw-r--r-- | internal/nametransform/pad16.go | 60 | ||||
| -rw-r--r-- | internal/toggledlog/log.go (renamed from cryptfs/log.go) | 6 | ||||
| -rw-r--r-- | internal/toggledlog/log_go1.4.go (renamed from cryptfs/log_go1.4.go) | 2 | ||||
| -rw-r--r-- | internal/toggledlog/log_go1.5.go (renamed from cryptfs/log_go1.5.go) | 4 | ||||
| -rw-r--r-- | main.go | 106 | ||||
| -rw-r--r-- | masterkey.go | 10 | ||||
| -rw-r--r-- | pathfs_frontend/file.go | 112 | ||||
| -rw-r--r-- | pathfs_frontend/file_holes.go | 13 | ||||
| -rw-r--r-- | pathfs_frontend/fs.go | 76 | ||||
| -rw-r--r-- | pathfs_frontend/fs_dir.go | 44 | ||||
| -rw-r--r-- | pathfs_frontend/names.go | 17 | ||||
| -rwxr-xr-x | test.bash | 2 | 
43 files changed, 579 insertions, 513 deletions
| diff --git a/cryptfs/cryptfs.go b/cryptfs/cryptfs.go deleted file mode 100644 index 3a40e29..0000000 --- a/cryptfs/cryptfs.go +++ /dev/null @@ -1,83 +0,0 @@ -package cryptfs - -// CryptFS is the crypto backend of GoCryptFS - -import ( -	"crypto/aes" -	"crypto/cipher" -	"fmt" -) - -const ( -	PROGRAM_NAME = "gocryptfs" - -	DEFAULT_PLAINBS = 4096 -	KEY_LEN         = 32 // AES-256 -	AUTH_TAG_LEN    = 16 -	DIRIV_LEN       = 16 // identical to AES block size -	DIRIV_FILENAME  = "gocryptfs.diriv" -) - -type CryptFS struct { -	blockCipher cipher.Block -	gcm         cipher.AEAD -	gcmIVLen    int -	gcmIVGen    nonceGenerator -	plainBS     uint64 -	cipherBS    uint64 -	// Stores an all-zero block of size cipherBS -	allZeroBlock []byte -	// DirIV cache for filename encryption -	DirIVCache dirIVCache -} - -func NewCryptFS(key []byte, useOpenssl bool, plaintextNames bool, GCMIV128 bool) *CryptFS { - -	if len(key) != KEY_LEN { -		panic(fmt.Sprintf("Unsupported key length %d", len(key))) -	} - -	b, err := aes.NewCipher(key) -	if err != nil { -		panic(err) -	} - -	// We want the IV size in bytes -	gcmIV := 96 / 8 -	if GCMIV128 { -		gcmIV = 128 / 8 -	} - -	var gcm cipher.AEAD -	if useOpenssl { -		gcm = opensslGCM{key} -	} else { -		gcm, err = goGCMWrapper(b, gcmIV) -		if err != nil { -			panic(err) -		} -	} - -	plainBS := DEFAULT_PLAINBS -	cipherBS := plainBS + gcmIV + AUTH_TAG_LEN - -	return &CryptFS{ -		blockCipher:  b, -		gcm:          gcm, -		gcmIVLen:     gcmIV, -		gcmIVGen:     nonceGenerator{nonceLen: gcmIV}, -		plainBS:      uint64(plainBS), -		cipherBS:     uint64(cipherBS), -		allZeroBlock: make([]byte, cipherBS), -	} -} - -// Get plaintext block size -func (be *CryptFS) PlainBS() uint64 { -	return be.plainBS -} - -// Per-block storage overhead -func (be *CryptFS) BlockOverhead() uint64 { -	return be.cipherBS - be.plainBS -} diff --git a/cryptfs/names_core.go b/cryptfs/names_core.go deleted file mode 100644 index 0f2e5b3..0000000 --- a/cryptfs/names_core.go +++ /dev/null @@ -1,134 +0,0 @@ -package cryptfs - -// Filename encryption / decryption functions - -import ( -	"crypto/aes" -	"crypto/cipher" -	"encoding/base64" -	"errors" -	"fmt" - -	"github.com/rfjakob/eme" -) - -// DecryptName - decrypt base64-encoded encrypted filename "cipherName" -// The used encryption is either CBC or EME, depending on the "EMENames" argument. -// -// This function is exported because it allows for a very efficient readdir -// implementation (read IV once, decrypt all names using this function). -func (be *CryptFS) DecryptName(cipherName string, iv []byte, EMENames bool) (string, error) { -	return be.decryptName(cipherName, iv, EMENames) -} - -// decryptName - decrypt base64-encoded encrypted filename "cipherName". -// The used encryption is either CBC or EME, depending on the "EMENames" argument. -func (be *CryptFS) decryptName(cipherName string, iv []byte, EMENames bool) (string, error) { - -	// Make sure relative symlinks still work after encryption -	// by passing these through unchanged -	if cipherName == "." || cipherName == ".." { -		return cipherName, nil -	} - -	bin, err := base64.URLEncoding.DecodeString(cipherName) -	if err != nil { -		return "", err -	} - -	if len(bin)%aes.BlockSize != 0 { -		return "", fmt.Errorf("Decoded length %d is not a multiple of the AES block size", len(bin)) -	} - -	if EMENames { -		bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionDecrypt) -	} else { -		cbc := cipher.NewCBCDecrypter(be.blockCipher, iv) -		cbc.CryptBlocks(bin, bin) -	} - -	bin, err = be.unPad16(bin) -	if err != nil { -		return "", err -	} - -	plain := string(bin) -	return plain, err -} - -// encryptName - encrypt "plainName", return base64-encoded "cipherName64" -// The used encryption is either CBC or EME, depending on the "EMENames" argument. -func (be *CryptFS) encryptName(plainName string, iv []byte, EMENames bool) (cipherName64 string) { - -	// Make sure relative symlinks still work after encryption -	// by passing these trough unchanged -	if plainName == "." || plainName == ".." { -		return plainName -	} - -	bin := []byte(plainName) -	bin = be.pad16(bin) - -	if EMENames { -		bin = eme.Transform(be.blockCipher, iv, bin, eme.DirectionEncrypt) -	} else { -		cbc := cipher.NewCBCEncrypter(be.blockCipher, iv) -		cbc.CryptBlocks(bin, bin) -	} - -	cipherName64 = base64.URLEncoding.EncodeToString(bin) -	return cipherName64 -} - -// pad16 - pad filename to 16 byte blocks using standard PKCS#7 padding -// https://tools.ietf.org/html/rfc5652#section-6.3 -func (be *CryptFS) pad16(orig []byte) (padded []byte) { -	oldLen := len(orig) -	if oldLen == 0 { -		panic("Padding zero-length string makes no sense") -	} -	padLen := aes.BlockSize - oldLen%aes.BlockSize -	if padLen == 0 { -		padLen = aes.BlockSize -	} -	newLen := oldLen + padLen -	padded = make([]byte, newLen) -	copy(padded, orig) -	padByte := byte(padLen) -	for i := oldLen; i < newLen; i++ { -		padded[i] = padByte -	} -	return padded -} - -// unPad16 - remove padding -func (be *CryptFS) unPad16(orig []byte) ([]byte, error) { -	oldLen := len(orig) -	if oldLen%aes.BlockSize != 0 { -		return nil, errors.New("Unaligned size") -	} -	// The last byte is always a padding byte -	padByte := orig[oldLen-1] -	// The padding byte's value is the padding length -	padLen := int(padByte) -	// Padding must be at least 1 byte -	if padLen <= 0 { -		return nil, errors.New("Padding cannot be zero-length") -	} -	// Larger paddings make no sense -	if padLen > aes.BlockSize { -		return nil, errors.New("Padding cannot be larger than 16") -	} -	// All padding bytes must be identical -	for i := oldLen - padLen; i < oldLen; i++ { -		if orig[i] != padByte { -			return nil, errors.New(fmt.Sprintf("Padding byte at i=%d is invalid", i)) -		} -	} -	newLen := oldLen - padLen -	// Padding an empty string makes no sense -	if newLen == 0 { -		return nil, errors.New("Unpadded length is zero") -	} -	return orig[0:newLen], nil -} diff --git a/integration_tests/cli_test.go b/integration_tests/cli_test.go index 5e8902d..062a90d 100644 --- a/integration_tests/cli_test.go +++ b/integration_tests/cli_test.go @@ -7,7 +7,8 @@ import (  	"os/exec"  	"testing" -	"github.com/rfjakob/gocryptfs/cryptfs" +	"github.com/rfjakob/gocryptfs/internal/configfile" +	"github.com/rfjakob/gocryptfs/internal/nametransform"  )  // Test -init flag @@ -26,7 +27,7 @@ func TestInit(t *testing.T) {  	if err != nil {  		t.Fatal(err)  	} -	_, err = os.Stat(dir + cryptfs.ConfDefaultName) +	_, err = os.Stat(dir + configfile.ConfDefaultName)  	if err != nil {  		t.Fatal(err)  	} @@ -96,22 +97,22 @@ func TestInitPlaintextNames(t *testing.T) {  	if err != nil {  		t.Fatal(err)  	} -	_, err = os.Stat(dir + cryptfs.ConfDefaultName) +	_, err = os.Stat(dir + configfile.ConfDefaultName)  	if err != nil {  		t.Fatal(err)  	} -	_, err = os.Stat(dir + cryptfs.DIRIV_FILENAME) +	_, err = os.Stat(dir + nametransform.DirIVFilename)  	if err == nil {  		t.Errorf("gocryptfs.diriv should not have been created with -plaintextnames")  	} -	_, cf, err := cryptfs.LoadConfFile(dir+cryptfs.ConfDefaultName, "test") +	_, cf, err := configfile.LoadConfFile(dir+configfile.ConfDefaultName, "test")  	if err != nil {  		t.Fatal(err)  	} -	if !cf.IsFeatureFlagSet(cryptfs.FlagPlaintextNames) { +	if !cf.IsFeatureFlagSet(configfile.FlagPlaintextNames) {  		t.Error("PlaintextNames flag should be set but isnt")  	} -	if cf.IsFeatureFlagSet(cryptfs.FlagEMENames) || cf.IsFeatureFlagSet(cryptfs.FlagDirIV) { +	if cf.IsFeatureFlagSet(configfile.FlagEMENames) || cf.IsFeatureFlagSet(configfile.FlagDirIV) {  		t.Error("FlagEMENames and FlagDirIV should be not set")  	}  } diff --git a/integration_tests/helpers.go b/integration_tests/helpers.go index 5145b30..e5458c9 100644 --- a/integration_tests/helpers.go +++ b/integration_tests/helpers.go @@ -10,7 +10,7 @@ import (  	"syscall"  	"testing" -	"github.com/rfjakob/gocryptfs/cryptfs" +	"github.com/rfjakob/gocryptfs/internal/nametransform"  )  // Note: the code assumes that all have a trailing slash @@ -42,7 +42,7 @@ func resetTmpDir() {  		fmt.Println(err)  		os.Exit(1)  	} -	err = cryptfs.WriteDirIV(defaultCipherDir) +	err = nametransform.WriteDirIV(defaultCipherDir)  	if err != nil {  		fmt.Println(err)  		os.Exit(1) @@ -58,9 +58,9 @@ func mount(c string, p string, extraArgs ...string) {  	args = append(args, c)  	args = append(args, p)  	cmd := exec.Command(gocryptfsBinary, args...) +	cmd.Stderr = os.Stderr  	if testing.Verbose() {  		cmd.Stdout = os.Stdout -		cmd.Stderr = os.Stderr  	}  	err := cmd.Run()  	if err != nil { diff --git a/cryptfs/config_file.go b/internal/configfile/config_file.go index 013b82d..0128acc 100644 --- a/cryptfs/config_file.go +++ b/internal/configfile/config_file.go @@ -1,10 +1,14 @@ -package cryptfs +package configfile  import (  	"encoding/json"  	"fmt"  	"io/ioutil"  	"log" + +	"github.com/rfjakob/gocryptfs/internal/cryptocore" +	"github.com/rfjakob/gocryptfs/internal/contentenc" +	"github.com/rfjakob/gocryptfs/internal/toggledlog"  )  import "os" @@ -36,10 +40,10 @@ type ConfFile struct {  func CreateConfFile(filename string, password string, plaintextNames bool, logN int) error {  	var cf ConfFile  	cf.filename = filename -	cf.Version = HEADER_CURRENT_VERSION +	cf.Version = contentenc.CurrentVersion  	// Generate new random master key -	key := RandBytes(KEY_LEN) +	key := cryptocore.RandBytes(cryptocore.KeyLen)  	// Encrypt it using the password  	// This sets ScryptObject and EncryptedKey @@ -75,11 +79,11 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {  	// Unmarshal  	err = json.Unmarshal(js, &cf)  	if err != nil { -		Warn.Printf("Failed to unmarshal config file") +		toggledlog.Warn.Printf("Failed to unmarshal config file")  		return nil, nil, err  	} -	if cf.Version != HEADER_CURRENT_VERSION { +	if cf.Version != contentenc.CurrentVersion {  		return nil, nil, fmt.Errorf("Unsupported on-disk format %d", cf.Version)  	} @@ -95,11 +99,13 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {  	// Unlock master key using password-based key  	// We use stock go GCM instead of OpenSSL here as speed is not important  	// and we get better error messages -	cfs := NewCryptFS(scryptHash, false, false, false) -	key, err := cfs.DecryptBlock(cf.EncryptedKey, 0, nil) +	cc := cryptocore.New(scryptHash, false, false) +	ce := contentenc.New(cc, 4096) + +	key, err := ce.DecryptBlock(cf.EncryptedKey, 0, nil)  	if err != nil { -		Warn.Printf("failed to unlock master key: %s", err.Error()) -		Warn.Printf("Password incorrect.") +		toggledlog.Warn.Printf("failed to unlock master key: %s", err.Error()) +		toggledlog.Warn.Printf("Password incorrect.")  		return nil, nil, err  	} @@ -116,8 +122,9 @@ func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {  	scryptHash := cf.ScryptObject.DeriveKey(password)  	// Lock master key using password-based key -	cfs := NewCryptFS(scryptHash, false, false, false) -	cf.EncryptedKey = cfs.EncryptBlock(key, 0, nil) +	cc := cryptocore.New(scryptHash, false, false) +	ce := contentenc.New(cc, 4096) +	cf.EncryptedKey = ce.EncryptBlock(key, 0, nil)  }  // WriteFile - write out config in JSON format to file "filename.tmp" diff --git a/cryptfs/config_test.go b/internal/configfile/config_test.go index 11599c0..6606d22 100644 --- a/cryptfs/config_test.go +++ b/internal/configfile/config_test.go @@ -1,4 +1,4 @@ -package cryptfs +package configfile  import (  	"fmt" diff --git a/cryptfs/config_test/.gitignore b/internal/configfile/config_test/.gitignore index 0720169..0720169 100644 --- a/cryptfs/config_test/.gitignore +++ b/internal/configfile/config_test/.gitignore diff --git a/cryptfs/config_test/PlaintextNames.conf b/internal/configfile/config_test/PlaintextNames.conf index c1ff8cc..c1ff8cc 100644 --- a/cryptfs/config_test/PlaintextNames.conf +++ b/internal/configfile/config_test/PlaintextNames.conf diff --git a/cryptfs/config_test/StrangeFeature.conf b/internal/configfile/config_test/StrangeFeature.conf index 6a97781..6a97781 100644 --- a/cryptfs/config_test/StrangeFeature.conf +++ b/internal/configfile/config_test/StrangeFeature.conf diff --git a/cryptfs/config_test/v1.conf b/internal/configfile/config_test/v1.conf index 588a25a..588a25a 100644 --- a/cryptfs/config_test/v1.conf +++ b/internal/configfile/config_test/v1.conf diff --git a/cryptfs/config_test/v2.conf b/internal/configfile/config_test/v2.conf index 8ef3dcf..8ef3dcf 100644 --- a/cryptfs/config_test/v2.conf +++ b/internal/configfile/config_test/v2.conf diff --git a/cryptfs/kdf.go b/internal/configfile/kdf.go index e958413..f1a7a40 100644 --- a/cryptfs/kdf.go +++ b/internal/configfile/kdf.go @@ -1,16 +1,19 @@ -package cryptfs +package configfile  import (  	"fmt" -	"golang.org/x/crypto/scrypt"  	"math"  	"os" + +	"golang.org/x/crypto/scrypt" + +	"github.com/rfjakob/gocryptfs/internal/cryptocore"  )  const (  	// 1 << 16 uses 64MB of memory,  	// takes 4 seconds on my Atom Z3735F netbook -	SCRYPT_DEFAULT_LOGN = 16 +	ScryptDefaultLogN = 16  )  type scryptKdf struct { @@ -23,9 +26,9 @@ type scryptKdf struct {  func NewScryptKdf(logN int) scryptKdf {  	var s scryptKdf -	s.Salt = RandBytes(KEY_LEN) +	s.Salt = cryptocore.RandBytes(cryptocore.KeyLen)  	if logN <= 0 { -		s.N = 1 << SCRYPT_DEFAULT_LOGN +		s.N = 1 << ScryptDefaultLogN  	} else {  		if logN < 10 {  			fmt.Println("Error: scryptn below 10 is too low to make sense. Aborting.") @@ -35,7 +38,7 @@ func NewScryptKdf(logN int) scryptKdf {  	}  	s.R = 8 // Always 8  	s.P = 1 // Always 1 -	s.KeyLen = KEY_LEN +	s.KeyLen = cryptocore.KeyLen  	return s  } diff --git a/cryptfs/kdf_test.go b/internal/configfile/kdf_test.go index 4d909ea..bc095ab 100644 --- a/cryptfs/kdf_test.go +++ b/internal/configfile/kdf_test.go @@ -1,4 +1,4 @@ -package cryptfs +package configfile  import (  	"testing" diff --git a/cryptfs/cryptfs_content.go b/internal/contentenc/content.go index 2036e58..14135a2 100644 --- a/cryptfs/cryptfs_content.go +++ b/internal/contentenc/content.go @@ -1,31 +1,18 @@ -package cryptfs +package contentenc  // File content encryption / decryption  import ( -	"bytes" -	"crypto/cipher" -	"crypto/md5"  	"encoding/binary" +	"bytes"  	"encoding/hex"  	"errors" -	"os" -) -// md5sum - debug helper, return md5 hex string -func md5sum(buf []byte) string { -	rawHash := md5.Sum(buf) -	hash := hex.EncodeToString(rawHash[:]) -	return hash -} - -type CryptFile struct { -	file *os.File -	gcm  cipher.AEAD -} +	"github.com/rfjakob/gocryptfs/internal/toggledlog" +)  // DecryptBlocks - Decrypt a number of blocks -func (be *CryptFS) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileId []byte) ([]byte, error) { +func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileId []byte) ([]byte, error) {  	cBuf := bytes.NewBuffer(ciphertext)  	var err error  	var pBuf bytes.Buffer @@ -46,7 +33,7 @@ func (be *CryptFS) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, fileId  //  // Corner case: A full-sized block of all-zero ciphertext bytes is translated  // to an all-zero plaintext block, i.e. file hole passtrough. -func (be *CryptFS) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte) ([]byte, error) { +func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte) ([]byte, error) {  	// Empty block?  	if len(ciphertext) == 0 { @@ -55,30 +42,30 @@ func (be *CryptFS) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte  	// All-zero block?  	if bytes.Equal(ciphertext, be.allZeroBlock) { -		Debug.Printf("DecryptBlock: file hole encountered") +		toggledlog.Debug.Printf("DecryptBlock: file hole encountered")  		return make([]byte, be.plainBS), nil  	} -	if len(ciphertext) < be.gcmIVLen { -		Warn.Printf("DecryptBlock: Block is too short: %d bytes", len(ciphertext)) +	if len(ciphertext) < be.cryptoCore.IVLen { +		toggledlog.Warn.Printf("DecryptBlock: Block is too short: %d bytes", len(ciphertext))  		return nil, errors.New("Block is too short")  	}  	// Extract nonce -	nonce := ciphertext[:be.gcmIVLen] +	nonce := ciphertext[:be.cryptoCore.IVLen]  	ciphertextOrig := ciphertext -	ciphertext = ciphertext[be.gcmIVLen:] +	ciphertext = ciphertext[be.cryptoCore.IVLen:]  	// Decrypt  	var plaintext []byte  	aData := make([]byte, 8)  	aData = append(aData, fileId...)  	binary.BigEndian.PutUint64(aData, blockNo) -	plaintext, err := be.gcm.Open(plaintext, nonce, ciphertext, aData) +	plaintext, err := be.cryptoCore.Gcm.Open(plaintext, nonce, ciphertext, aData)  	if err != nil { -		Warn.Printf("DecryptBlock: %s, len=%d, md5=%s", err.Error(), len(ciphertextOrig), md5sum(ciphertextOrig)) -		Debug.Println(hex.Dump(ciphertextOrig)) +		toggledlog.Warn.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig)) +		toggledlog.Debug.Println(hex.Dump(ciphertextOrig))  		return nil, err  	} @@ -86,7 +73,7 @@ func (be *CryptFS) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []byte  }  // encryptBlock - Encrypt and add IV and MAC -func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte) []byte { +func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte) []byte {  	// Empty block?  	if len(plaintext) == 0 { @@ -94,7 +81,7 @@ func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte)  	}  	// Get fresh nonce -	nonce := be.gcmIVGen.Get() +	nonce := be.cryptoCore.GcmIVGen.Get()  	// Authenticate block with block number and file ID  	aData := make([]byte, 8) @@ -102,14 +89,14 @@ func (be *CryptFS) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte)  	aData = append(aData, fileID...)  	// Encrypt plaintext and append to nonce -	ciphertext := be.gcm.Seal(nonce, nonce, plaintext, aData) +	ciphertext := be.cryptoCore.Gcm.Seal(nonce, nonce, plaintext, aData)  	return ciphertext  }  // MergeBlocks - Merge newData into oldData at offset  // New block may be bigger than both newData and oldData -func (be *CryptFS) MergeBlocks(oldData []byte, newData []byte, offset int) []byte { +func (be *ContentEnc) MergeBlocks(oldData []byte, newData []byte, offset int) []byte {  	// Make block of maximum size  	out := make([]byte, be.plainBS) diff --git a/internal/contentenc/content_api.go b/internal/contentenc/content_api.go new file mode 100644 index 0000000..1700d35 --- /dev/null +++ b/internal/contentenc/content_api.go @@ -0,0 +1,31 @@ +package contentenc + +import "github.com/rfjakob/gocryptfs/internal/cryptocore" + +type ContentEnc struct { +	// Cryptographic primitives +	cryptoCore *cryptocore.CryptoCore +	// Plaintext block size +	plainBS     uint64 +	// Ciphertext block size +	cipherBS    uint64 +	// All-zero block of size cipherBS, for fast compares +	allZeroBlock []byte +} + +func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc { + +	cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen + +	return &ContentEnc{ +		cryptoCore: cc, +		plainBS: plainBS, +		cipherBS: cipherBS, +		allZeroBlock: make([]byte, cipherBS), +	} +} + + +func (be *ContentEnc) PlainBS() uint64 { +	return be.plainBS +} diff --git a/cryptfs/content_test.go b/internal/contentenc/content_test.go index 3efa959..70ad58d 100644 --- a/cryptfs/content_test.go +++ b/internal/contentenc/content_test.go @@ -1,4 +1,4 @@ -package cryptfs +package contentenc  import (  	"testing" diff --git a/cryptfs/file_header.go b/internal/contentenc/file_header.go index fe4ac56..8a9dd2c 100644 --- a/cryptfs/file_header.go +++ b/internal/contentenc/file_header.go @@ -1,4 +1,4 @@ -package cryptfs +package contentenc  // Per-file header  // @@ -7,10 +7,14 @@ package cryptfs  import (  	"encoding/binary"  	"fmt" + +	"github.com/rfjakob/gocryptfs/internal/cryptocore"  )  const ( -	HEADER_CURRENT_VERSION = 2                                  // Current on-disk-format version +	// Current On-Disk-Format version +	CurrentVersion = 2 +  	HEADER_VERSION_LEN     = 2                                  // uint16  	HEADER_ID_LEN          = 16                                 // 128 bit random file id  	HEADER_LEN             = HEADER_VERSION_LEN + HEADER_ID_LEN // Total header length @@ -23,7 +27,7 @@ type FileHeader struct {  // Pack - serialize fileHeader object  func (h *FileHeader) Pack() []byte { -	if len(h.Id) != HEADER_ID_LEN || h.Version != HEADER_CURRENT_VERSION { +	if len(h.Id) != HEADER_ID_LEN || h.Version != CurrentVersion {  		panic("FileHeader object not properly initialized")  	}  	buf := make([]byte, HEADER_LEN) @@ -40,8 +44,8 @@ func ParseHeader(buf []byte) (*FileHeader, error) {  	}  	var h FileHeader  	h.Version = binary.BigEndian.Uint16(buf[0:HEADER_VERSION_LEN]) -	if h.Version != HEADER_CURRENT_VERSION { -		return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", h.Version, HEADER_CURRENT_VERSION) +	if h.Version != CurrentVersion { +		return nil, fmt.Errorf("ParseHeader: invalid version: got %d, want %d", h.Version, CurrentVersion)  	}  	h.Id = buf[HEADER_VERSION_LEN:]  	return &h, nil @@ -50,7 +54,7 @@ func ParseHeader(buf []byte) (*FileHeader, error) {  // RandomHeader - create new fileHeader object with random Id  func RandomHeader() *FileHeader {  	var h FileHeader -	h.Version = HEADER_CURRENT_VERSION -	h.Id = RandBytes(HEADER_ID_LEN) +	h.Version = CurrentVersion +	h.Id = cryptocore.RandBytes(HEADER_ID_LEN)  	return &h  } diff --git a/cryptfs/intrablock.go b/internal/contentenc/intrablock.go index faff471..330b980 100644 --- a/cryptfs/intrablock.go +++ b/internal/contentenc/intrablock.go @@ -1,11 +1,11 @@ -package cryptfs +package contentenc  // intraBlock identifies a part of a file block  type intraBlock struct {  	BlockNo uint64 // Block number in file  	Skip    uint64 // Offset into block plaintext  	Length  uint64 // Length of data from this block -	fs      *CryptFS +	fs      *ContentEnc  }  // isPartial - is the block partial? This means we have to do read-modify-write. diff --git a/cryptfs/address_translation.go b/internal/contentenc/offsets.go index b21cfc7..1b5952f 100644 --- a/cryptfs/address_translation.go +++ b/internal/contentenc/offsets.go @@ -1,29 +1,33 @@ -package cryptfs +package contentenc -// CryptFS methods that translate offsets between ciphertext and plaintext +import ( +	"github.com/rfjakob/gocryptfs/internal/toggledlog" +) + +// Contentenc methods that translate offsets between ciphertext and plaintext  // get the block number at plain-text offset -func (be *CryptFS) PlainOffToBlockNo(plainOffset uint64) uint64 { +func (be *ContentEnc) PlainOffToBlockNo(plainOffset uint64) uint64 {  	return plainOffset / be.plainBS  }  // get the block number at ciphter-text offset -func (be *CryptFS) CipherOffToBlockNo(cipherOffset uint64) uint64 { +func (be *ContentEnc) CipherOffToBlockNo(cipherOffset uint64) uint64 {  	return (cipherOffset - HEADER_LEN) / be.cipherBS  }  // get ciphertext offset of block "blockNo" -func (be *CryptFS) BlockNoToCipherOff(blockNo uint64) uint64 { +func (be *ContentEnc) BlockNoToCipherOff(blockNo uint64) uint64 {  	return HEADER_LEN + blockNo*be.cipherBS  }  // get plaintext offset of block "blockNo" -func (be *CryptFS) BlockNoToPlainOff(blockNo uint64) uint64 { +func (be *ContentEnc) BlockNoToPlainOff(blockNo uint64) uint64 {  	return blockNo * be.plainBS  }  // PlainSize - calculate plaintext size from ciphertext size -func (be *CryptFS) CipherSizeToPlainSize(cipherSize uint64) uint64 { +func (be *ContentEnc) CipherSizeToPlainSize(cipherSize uint64) uint64 {  	// Zero sized files stay zero-sized  	if cipherSize == 0 { @@ -31,12 +35,12 @@ func (be *CryptFS) CipherSizeToPlainSize(cipherSize uint64) uint64 {  	}  	if cipherSize == HEADER_LEN { -		Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize) +		toggledlog.Warn.Printf("cipherSize %d == header size: interrupted write?\n", cipherSize)  		return 0  	}  	if cipherSize < HEADER_LEN { -		Warn.Printf("cipherSize %d < header size: corrupt file\n", cipherSize) +		toggledlog.Warn.Printf("cipherSize %d < header size: corrupt file\n", cipherSize)  		return 0  	} @@ -50,7 +54,7 @@ func (be *CryptFS) CipherSizeToPlainSize(cipherSize uint64) uint64 {  }  // CipherSize - calculate ciphertext size from plaintext size -func (be *CryptFS) PlainSizeToCipherSize(plainSize uint64) uint64 { +func (be *ContentEnc) PlainSizeToCipherSize(plainSize uint64) uint64 {  	// Block number at last byte  	blockNo := be.PlainOffToBlockNo(plainSize - 1) @@ -62,7 +66,7 @@ func (be *CryptFS) PlainSizeToCipherSize(plainSize uint64) uint64 {  }  // Split a plaintext byte range into (possibly partial) blocks -func (be *CryptFS) ExplodePlainRange(offset uint64, length uint64) []intraBlock { +func (be *ContentEnc) ExplodePlainRange(offset uint64, length uint64) []intraBlock {  	var blocks []intraBlock  	var nextBlock intraBlock  	nextBlock.fs = be @@ -81,6 +85,10 @@ func (be *CryptFS) ExplodePlainRange(offset uint64, length uint64) []intraBlock  	return blocks  } +func (be *ContentEnc) BlockOverhead() uint64 { +	return be.cipherBS - be.plainBS +} +  func MinUint64(x uint64, y uint64) uint64 {  	if x < y {  		return x diff --git a/internal/cryptocore/crypto_api.go b/internal/cryptocore/crypto_api.go new file mode 100644 index 0000000..c6b6869 --- /dev/null +++ b/internal/cryptocore/crypto_api.go @@ -0,0 +1,56 @@ +package cryptocore + +import ( +	"crypto/cipher" +	"crypto/aes" +	"fmt" +) + +const ( +	KeyLen          = 32 // AES-256 +	AuthTagLen      = 16 +) + +type CryptoCore struct { +	BlockCipher cipher.Block +	Gcm         cipher.AEAD +	GcmIVGen	*nonceGenerator +	IVLen		int +} + +func New(key []byte, useOpenssl bool, GCMIV128 bool) *CryptoCore { + +	if len(key) != KeyLen { +		panic(fmt.Sprintf("Unsupported key length %d", len(key))) +	} + +	// We want the IV size in bytes +	IVLen := 96 / 8 +	if GCMIV128 { +		IVLen = 128 / 8 +	} + +	// We always use built-in Go crypto for blockCipher because it is not +	// performance-critical. +	blockCipher, err := aes.NewCipher(key) +	if err != nil { +		panic(err) +	} + +	var gcm cipher.AEAD +	if useOpenssl { +		gcm = opensslGCM{key} +	} else { +		gcm, err = goGCMWrapper(blockCipher, IVLen) +		if err != nil { +			panic(err) +		} +	} + +	return &CryptoCore{ +		BlockCipher: blockCipher, +		Gcm: gcm, +		GcmIVGen:  &nonceGenerator{nonceLen: IVLen}, +		IVLen: IVLen, +	} +} diff --git a/cryptfs/gcm_go1.4.go b/internal/cryptocore/gcm_go1.4.go index 0a2ff49..dba222c 100644 --- a/cryptfs/gcm_go1.4.go +++ b/internal/cryptocore/gcm_go1.4.go @@ -1,7 +1,7 @@  // +build !go1.5  // = go 1.4 or lower -package cryptfs +package cryptocore  import (  	"crypto/cipher" diff --git a/cryptfs/gcm_go1.5.go b/internal/cryptocore/gcm_go1.5.go index c469357..0c9b1a5 100644 --- a/cryptfs/gcm_go1.5.go +++ b/internal/cryptocore/gcm_go1.5.go @@ -1,7 +1,7 @@  // +build go1.5  // = go 1.5 or higher -package cryptfs +package cryptocore  import (  	"crypto/cipher" diff --git a/cryptfs/nonce.go b/internal/cryptocore/nonce.go index be777fc..72d8588 100644 --- a/cryptfs/nonce.go +++ b/internal/cryptocore/nonce.go @@ -1,4 +1,4 @@ -package cryptfs +package cryptocore  import (  	"bytes" @@ -6,6 +6,8 @@ import (  	"encoding/binary"  	"encoding/hex"  	"fmt" + +	"github.com/rfjakob/gocryptfs/internal/toggledlog"  )  // Get "n" random bytes from /dev/urandom or panic @@ -29,10 +31,10 @@ type nonceGenerator struct {  	nonceLen  int // bytes  } -// Get a random 96 bit nonce +// Get a random "nonceLen"-byte nonce  func (n *nonceGenerator) Get() []byte {  	nonce := RandBytes(n.nonceLen) -	Debug.Printf("nonceGenerator.Get(): %s\n", hex.EncodeToString(nonce)) +	toggledlog.Debug.Printf("nonceGenerator.Get(): %s\n", hex.EncodeToString(nonce))  	if bytes.Equal(nonce, n.lastNonce) {  		m := fmt.Sprintf("Got the same nonce twice: %s. This should never happen!", hex.EncodeToString(nonce))  		panic(m) diff --git a/cryptfs/openssl_aead.go b/internal/cryptocore/openssl_aead.go index 5d38d38..d4ed64b 100644 --- a/cryptfs/openssl_aead.go +++ b/internal/cryptocore/openssl_aead.go @@ -1,4 +1,4 @@ -package cryptfs +package cryptocore  // Implements cipher.AEAD with OpenSSL backend @@ -13,7 +13,7 @@ type opensslGCM struct {  }  func (be opensslGCM) Overhead() int { -	return AUTH_TAG_LEN +	return AuthTagLen  }  func (be opensslGCM) NonceSize() int { @@ -28,11 +28,11 @@ func (be opensslGCM) Seal(dst, nonce, plaintext, data []byte) []byte {  	// Preallocate output buffer  	var cipherBuf bytes.Buffer -	cipherBuf.Grow(len(dst) + len(plaintext) + AUTH_TAG_LEN) +	cipherBuf.Grow(len(dst) + len(plaintext) + AuthTagLen)  	// Output will be appended to dst  	cipherBuf.Write(dst) -	ectx, err := openssl.NewGCMEncryptionCipherCtx(KEY_LEN*8, nil, be.key, nonce) +	ectx, err := openssl.NewGCMEncryptionCipherCtx(KeyLen*8, nil, be.key, nonce)  	if err != nil {  		panic(err)  	} @@ -69,11 +69,11 @@ func (be opensslGCM) Seal(dst, nonce, plaintext, data []byte) []byte {  func (be opensslGCM) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {  	l := len(ciphertext) -	tag := ciphertext[l-AUTH_TAG_LEN : l] -	ciphertext = ciphertext[0 : l-AUTH_TAG_LEN] +	tag := ciphertext[l-AuthTagLen : l] +	ciphertext = ciphertext[0 : l-AuthTagLen]  	plainBuf := bytes.NewBuffer(dst) -	dctx, err := openssl.NewGCMDecryptionCipherCtx(KEY_LEN*8, nil, be.key, nonce) +	dctx, err := openssl.NewGCMDecryptionCipherCtx(KeyLen*8, nil, be.key, nonce)  	if err != nil {  		return nil, err  	} diff --git a/cryptfs/openssl_benchmark.bash b/internal/cryptocore/openssl_benchmark.bash index df29628..df29628 100755 --- a/cryptfs/openssl_benchmark.bash +++ b/internal/cryptocore/openssl_benchmark.bash diff --git a/cryptfs/openssl_test.go b/internal/cryptocore/openssl_test.go index aecee94..94b696a 100644 --- a/cryptfs/openssl_test.go +++ b/internal/cryptocore/openssl_test.go @@ -1,4 +1,4 @@ -package cryptfs +package cryptocore  // Benchmark go built-int GCM against spacemonkey openssl bindings  // diff --git a/internal/nametransform/name_api.go b/internal/nametransform/name_api.go new file mode 100644 index 0000000..462e99c --- /dev/null +++ b/internal/nametransform/name_api.go @@ -0,0 +1,16 @@ +package nametransform + +import "github.com/rfjakob/gocryptfs/internal/cryptocore" + +type NameTransform struct { +	cryptoCore *cryptocore.CryptoCore +	useEME bool +	DirIVCache dirIVCache +} + +func New(c *cryptocore.CryptoCore, useEME bool) *NameTransform { +	return &NameTransform{ +		cryptoCore: c, +		useEME: useEME, +	} +} diff --git a/internal/nametransform/names_core.go b/internal/nametransform/names_core.go new file mode 100644 index 0000000..452ab45 --- /dev/null +++ b/internal/nametransform/names_core.go @@ -0,0 +1,63 @@ +package nametransform + +// Filename encryption / decryption functions + +import ( +	"crypto/aes" +	"crypto/cipher" +	"encoding/base64" +	"fmt" + +	"github.com/rfjakob/eme" +) + +// DecryptName - decrypt base64-encoded encrypted filename "cipherName" +// The used encryption is either CBC or EME, depending on "useEME". +// +// This function is exported because it allows for a very efficient readdir +// implementation (read IV once, decrypt all names using this function). +func (n *NameTransform) DecryptName(cipherName string, iv []byte) (string, error) { + +	bin, err := base64.URLEncoding.DecodeString(cipherName) +	if err != nil { +		return "", err +	} + +	if len(bin)%aes.BlockSize != 0 { +		return "", fmt.Errorf("Decoded length %d is not a multiple of the AES block size", len(bin)) +	} + +	if n.useEME { +		bin = eme.Transform(n.cryptoCore.BlockCipher, iv, bin, eme.DirectionDecrypt) +	} else { +		cbc := cipher.NewCBCDecrypter(n.cryptoCore.BlockCipher, iv) +		cbc.CryptBlocks(bin, bin) +	} + +	bin, err = unPad16(bin) +	if err != nil { +		return "", err +	} + +	plain := string(bin) +	return plain, err +} + +// encryptName - encrypt "plainName", return base64-encoded "cipherName64" +// The used encryption is either CBC or EME, depending on "useEME". +func (n *NameTransform) encryptName(plainName string, iv []byte) (cipherName64 string) { + +	bin := []byte(plainName) +	bin = pad16(bin) + +	if n.useEME { +		bin = eme.Transform(n.cryptoCore.BlockCipher, iv, bin, eme.DirectionEncrypt) +	} else { +		cbc := cipher.NewCBCEncrypter(n.cryptoCore.BlockCipher, iv) +		cbc.CryptBlocks(bin, bin) +	} + +	cipherName64 = base64.URLEncoding.EncodeToString(bin) +	return cipherName64 +} + diff --git a/cryptfs/names_diriv.go b/internal/nametransform/names_diriv.go index 276316c..d31a066 100644 --- a/cryptfs/names_diriv.go +++ b/internal/nametransform/names_diriv.go @@ -1,4 +1,4 @@ -package cryptfs +package nametransform  import (  	"fmt" @@ -7,6 +7,17 @@ import (  	"path/filepath"  	"strings"  	"sync" + +	"github.com/rfjakob/gocryptfs/internal/toggledlog" +	"github.com/rfjakob/gocryptfs/internal/cryptocore" +) + +const ( +	// identical to AES block size +	dirIVLen       = 16 +	// dirIV is stored in this file. Exported because we have to ignore this +	// name in directory listing. +	DirIVFilename  = "gocryptfs.diriv"  )  // A simple one-entry DirIV cache @@ -50,23 +61,23 @@ func (c *dirIVCache) Clear() {  }  // readDirIV - read the "gocryptfs.diriv" file from "dir" (absolute ciphertext path) -func (be *CryptFS) ReadDirIV(dir string) (iv []byte, readErr error) { -	ivfile := filepath.Join(dir, DIRIV_FILENAME) -	Debug.Printf("ReadDirIV: reading %s\n", ivfile) +func (be *NameTransform) ReadDirIV(dir string) (iv []byte, readErr error) { +	ivfile := filepath.Join(dir, DirIVFilename) +	toggledlog.Debug.Printf("ReadDirIV: reading %s\n", ivfile)  	iv, readErr = ioutil.ReadFile(ivfile)  	if readErr != nil {  		// The directory may have been concurrently deleted or moved. Failure to  		// read the diriv is not an error in that case.  		_, statErr := os.Stat(dir)  		if os.IsNotExist(statErr) { -			Debug.Printf("ReadDirIV: Dir %s was deleted under our feet", dir) +			toggledlog.Debug.Printf("ReadDirIV: Dir %s was deleted under our feet", dir)  		} else {  			// This should not happen -			Warn.Printf("ReadDirIV: Dir exists but diriv does not: %v\n", readErr) +			toggledlog.Warn.Printf("ReadDirIV: Dir exists but diriv does not: %v\n", readErr)  		}  		return nil, readErr  	} -	if len(iv) != DIRIV_LEN { +	if len(iv) != dirIVLen {  		return nil, fmt.Errorf("ReadDirIV: Invalid length %d\n", len(iv))  	}  	return iv, nil @@ -76,14 +87,14 @@ func (be *CryptFS) ReadDirIV(dir string) (iv []byte, readErr error) {  // This function is exported because it is used from pathfs_frontend, main,  // and also the automated tests.  func WriteDirIV(dir string) error { -	iv := RandBytes(DIRIV_LEN) -	file := filepath.Join(dir, DIRIV_FILENAME) +	iv := cryptocore.RandBytes(dirIVLen) +	file := filepath.Join(dir, DirIVFilename)  	// 0444 permissions: the file is not secret but should not be written to  	return ioutil.WriteFile(file, iv, 0444)  }  // EncryptPathDirIV - encrypt path using EME with DirIV -func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool) (cipherPath string, err error) { +func (be *NameTransform) EncryptPathDirIV(plainPath string, rootDir string) (cipherPath string, err error) {  	// Empty string means root directory  	if plainPath == "" {  		return plainPath, nil @@ -94,7 +105,7 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool)  	if found {  		//fmt.Print("h")  		baseName := filepath.Base(plainPath) -		cBaseName := be.encryptName(baseName, iv, eme) +		cBaseName := be.encryptName(baseName, iv)  		cipherPath = cParentDir + "/" + cBaseName  		return cipherPath, nil  	} @@ -107,7 +118,7 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool)  		if err != nil {  			return "", err  		} -		encryptedName := be.encryptName(plainName, iv, eme) +		encryptedName := be.encryptName(plainName, iv)  		encryptedNames = append(encryptedNames, encryptedName)  		wd = filepath.Join(wd, encryptedName)  	} @@ -119,17 +130,17 @@ func (be *CryptFS) EncryptPathDirIV(plainPath string, rootDir string, eme bool)  }  // DecryptPathDirIV - decrypt path using EME with DirIV -func (be *CryptFS) DecryptPathDirIV(encryptedPath string, rootDir string, eme bool) (string, error) { +func (be *NameTransform) DecryptPathDirIV(encryptedPath string, rootDir string, eme bool) (string, error) {  	var wd = rootDir  	var plainNames []string  	encryptedNames := strings.Split(encryptedPath, "/") -	Debug.Printf("DecryptPathDirIV: decrypting %v\n", encryptedNames) +	toggledlog.Debug.Printf("DecryptPathDirIV: decrypting %v\n", encryptedNames)  	for _, encryptedName := range encryptedNames {  		iv, err := be.ReadDirIV(wd)  		if err != nil {  			return "", err  		} -		plainName, err := be.decryptName(encryptedName, iv, eme) +		plainName, err := be.DecryptName(encryptedName, iv)  		if err != nil {  			return "", err  		} diff --git a/cryptfs/names_noiv.go b/internal/nametransform/names_noiv.go index 7eed4b8..f301e52 100644 --- a/cryptfs/names_noiv.go +++ b/internal/nametransform/names_noiv.go @@ -1,4 +1,4 @@ -package cryptfs +package nametransform  import (  	"strings" @@ -12,7 +12,7 @@ const (  // DecryptPathNoIV - decrypt path using CBC without any IV.  // This function is deprecated by the the more secure DirIV variant and only retained  // for compatability with old filesystems. -func (be *CryptFS) DecryptPathNoIV(cipherPath string) (plainPath string, err error) { +func (be *NameTransform) DecryptPathNoIV(cipherPath string) (plainPath string, err error) {  	plainPath, err = be.translatePathNoIV(cipherPath, OpDecrypt)  	return plainPath, err  } @@ -20,14 +20,14 @@ func (be *CryptFS) DecryptPathNoIV(cipherPath string) (plainPath string, err err  // EncryptPathNoIV - decrypt path using CBC without any IV.  // This function is deprecated by the the more secure DirIV variant and only retained  // for compatability with old filesystems. -func (be *CryptFS) EncryptPathNoIV(plainPath string) (cipherPath string) { +func (be *NameTransform) EncryptPathNoIV(plainPath string) (cipherPath string) {  	cipherPath, _ = be.translatePathNoIV(plainPath, OpEncrypt)  	return cipherPath  }  // translatePathZeroIV - encrypt or decrypt path using CBC with an all-zero IV.  // Just splits the string on "/" and hands the parts to encryptName() / decryptName() -func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) { +func (be *NameTransform) translatePathNoIV(path string, op int) (string, error) {  	var err error  	// Empty string means root directory @@ -35,7 +35,7 @@ func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) {  		return path, err  	} -	zeroIV := make([]byte, DIRIV_LEN) +	zeroIV := make([]byte, dirIVLen)  	// Run operation on each path component  	var translatedParts []string @@ -49,9 +49,9 @@ func (be *CryptFS) translatePathNoIV(path string, op int) (string, error) {  		}  		var newPart string  		if op == OpEncrypt { -			newPart = be.encryptName(part, zeroIV, false) +			newPart = be.encryptName(part, zeroIV)  		} else { -			newPart, err = be.decryptName(part, zeroIV, false) +			newPart, err = be.DecryptName(part, zeroIV)  			if err != nil {  				return "", err  			} diff --git a/cryptfs/names_test.go b/internal/nametransform/names_test.go index 0207f0a..4a901be 100644 --- a/cryptfs/names_test.go +++ b/internal/nametransform/names_test.go @@ -1,4 +1,4 @@ -package cryptfs +package nametransform  import (  	"bytes" diff --git a/internal/nametransform/pad16.go b/internal/nametransform/pad16.go new file mode 100644 index 0000000..c15160e --- /dev/null +++ b/internal/nametransform/pad16.go @@ -0,0 +1,60 @@ +package nametransform + +import ( +	"fmt" +	"crypto/aes" +	"errors" +) + +// pad16 - pad data to AES block size (=16 byte) using standard PKCS#7 padding +// https://tools.ietf.org/html/rfc5652#section-6.3 +func pad16(orig []byte) (padded []byte) { +	oldLen := len(orig) +	if oldLen == 0 { +		panic("Padding zero-length string makes no sense") +	} +	padLen := aes.BlockSize - oldLen%aes.BlockSize +	if padLen == 0 { +		padLen = aes.BlockSize +	} +	newLen := oldLen + padLen +	padded = make([]byte, newLen) +	copy(padded, orig) +	padByte := byte(padLen) +	for i := oldLen; i < newLen; i++ { +		padded[i] = padByte +	} +	return padded +} + +// unPad16 - remove padding +func unPad16(padded []byte) ([]byte, error) { +	oldLen := len(padded) +	if oldLen%aes.BlockSize != 0 { +		return nil, errors.New("Unaligned size") +	} +	// The last byte is always a padding byte +	padByte := padded[oldLen-1] +	// The padding byte's value is the padding length +	padLen := int(padByte) +	// Padding must be at least 1 byte +	if padLen <= 0 { +		return nil, errors.New("Padding cannot be zero-length") +	} +	// Larger paddings make no sense +	if padLen > aes.BlockSize { +		return nil, fmt.Errorf("Padding too long, padLen = %d > 16", padLen) +	} +	// All padding bytes must be identical +	for i := oldLen - padLen; i < oldLen; i++ { +		if padded[i] != padByte { +			return nil, fmt.Errorf("Padding byte at i=%d is invalid", i) +		} +	} +	newLen := oldLen - padLen +	// Padding an empty string makes no sense +	if newLen == 0 { +		return nil, errors.New("Unpadded length is zero") +	} +	return padded[0:newLen], nil +} diff --git a/cryptfs/log.go b/internal/toggledlog/log.go index 19d8b51..4a2ad03 100644 --- a/cryptfs/log.go +++ b/internal/toggledlog/log.go @@ -1,4 +1,4 @@ -package cryptfs +package toggledlog  import (  	"encoding/json" @@ -7,6 +7,10 @@ import (  	"os"  ) +const ( +	ProgramName = "gocryptfs" +) +  func JSONDump(obj interface{}) string {  	b, err := json.MarshalIndent(obj, "", "\t")  	if err != nil { diff --git a/cryptfs/log_go1.4.go b/internal/toggledlog/log_go1.4.go index 4b91bad..4cdba44 100644 --- a/cryptfs/log_go1.4.go +++ b/internal/toggledlog/log_go1.4.go @@ -1,7 +1,7 @@  // +build !go1.5  // = go 1.4 or lower -package cryptfs +package toggledlog  import (  	"log/syslog" diff --git a/cryptfs/log_go1.5.go b/internal/toggledlog/log_go1.5.go index 8daae9c..e8e71f9 100644 --- a/cryptfs/log_go1.5.go +++ b/internal/toggledlog/log_go1.5.go @@ -1,14 +1,14 @@  // +build go1.5  // = go 1.5 or higher -package cryptfs +package toggledlog  import (  	"log/syslog"  )  func (l *toggledLogger) SwitchToSyslog(p syslog.Priority) { -	w, err := syslog.New(p, PROGRAM_NAME) +	w, err := syslog.New(p, ProgramName)  	if err != nil {  		Warn.Printf("Cannot switch 0x%02x to syslog: %v", p, err)  	} else { @@ -16,12 +16,16 @@ import (  	"golang.org/x/crypto/ssh/terminal" -	"github.com/rfjakob/gocryptfs/cryptfs" -	"github.com/rfjakob/gocryptfs/pathfs_frontend" -  	"github.com/hanwen/go-fuse/fuse"  	"github.com/hanwen/go-fuse/fuse/nodefs"  	"github.com/hanwen/go-fuse/fuse/pathfs" + +	"github.com/rfjakob/gocryptfs/pathfs_frontend" +	"github.com/rfjakob/gocryptfs/internal/configfile" +	"github.com/rfjakob/gocryptfs/internal/toggledlog" +	"github.com/rfjakob/gocryptfs/internal/nametransform" +	"github.com/rfjakob/gocryptfs/internal/contentenc" +	"github.com/rfjakob/gocryptfs/internal/cryptocore"  )  const ( @@ -56,9 +60,9 @@ func initDir(args *argContainer) {  	}  	// Create gocryptfs.conf -	cryptfs.Info.Printf("Choose a password for protecting your files.") +	toggledlog.Info.Printf("Choose a password for protecting your files.")  	password := readPasswordTwice(args.extpass) -	err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn) +	err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn)  	if err != nil {  		fmt.Println(err)  		os.Exit(ERREXIT_INIT) @@ -66,30 +70,30 @@ func initDir(args *argContainer) {  	if args.diriv && !args.plaintextnames {  		// Create gocryptfs.diriv in the root dir -		err = cryptfs.WriteDirIV(args.cipherdir) +		err = nametransform.WriteDirIV(args.cipherdir)  		if err != nil {  			fmt.Println(err)  			os.Exit(ERREXIT_INIT)  		}  	} -	cryptfs.Info.Printf(colorGreen + "The filesystem has been created successfully." + colorReset) -	cryptfs.Info.Printf(colorGrey+"You can now mount it using: %s %s MOUNTPOINT"+colorReset, -		cryptfs.PROGRAM_NAME, args.cipherdir) +	toggledlog.Info.Printf(colorGreen + "The filesystem has been created successfully." + colorReset) +	toggledlog.Info.Printf(colorGrey+"You can now mount it using: %s %s MOUNTPOINT"+colorReset, +		toggledlog.ProgramName, args.cipherdir)  	os.Exit(0)  }  func usageText() {  	printVersion()  	fmt.Printf("\n") -	fmt.Printf("Usage: %s -init|-passwd [OPTIONS] CIPHERDIR\n", cryptfs.PROGRAM_NAME) -	fmt.Printf("  or   %s [OPTIONS] CIPHERDIR MOUNTPOINT\n", cryptfs.PROGRAM_NAME) +	fmt.Printf("Usage: %s -init|-passwd [OPTIONS] CIPHERDIR\n", toggledlog.ProgramName) +	fmt.Printf("  or   %s [OPTIONS] CIPHERDIR MOUNTPOINT\n", toggledlog.ProgramName)  	fmt.Printf("\nOptions:\n")  	flagSet.PrintDefaults()  }  // loadConfig - load the config file "filename", prompting the user for the password -func loadConfig(args *argContainer) (masterkey []byte, confFile *cryptfs.ConfFile) { +func loadConfig(args *argContainer) (masterkey []byte, confFile *configfile.ConfFile) {  	// Check if the file exists at all before prompting for a password  	_, err := os.Stat(args.config)  	if err != nil { @@ -100,16 +104,16 @@ func loadConfig(args *argContainer) (masterkey []byte, confFile *cryptfs.ConfFil  		fmt.Printf("Password: ")  	}  	pw := readPassword(args.extpass) -	cryptfs.Info.Printf("Decrypting master key... ") -	cryptfs.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password -	masterkey, confFile, err = cryptfs.LoadConfFile(args.config, pw) -	cryptfs.Warn.Enabled = true +	toggledlog.Info.Printf("Decrypting master key... ") +	toggledlog.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password +	masterkey, confFile, err = configfile.LoadConfFile(args.config, pw) +	toggledlog.Warn.Enabled = true  	if err != nil {  		fmt.Println(err)  		fmt.Println(colorRed + "Wrong password." + colorReset)  		os.Exit(ERREXIT_LOADCONF)  	} -	cryptfs.Info.Printf("done.") +	toggledlog.Info.Printf("done.")  	return masterkey, confFile  } @@ -125,14 +129,14 @@ func changePassword(args *argContainer) {  		fmt.Println(err)  		os.Exit(ERREXIT_INIT)  	} -	cryptfs.Info.Printf("Password changed.") +	toggledlog.Info.Printf("Password changed.")  	os.Exit(0)  }  // printVersion - print a version string like  // "gocryptfs v0.3.1-31-g6736212-dirty; on-disk format 2"  func printVersion() { -	fmt.Printf("%s %s; on-disk format %d\n", cryptfs.PROGRAM_NAME, GitVersion, cryptfs.HEADER_CURRENT_VERSION) +	fmt.Printf("%s %s; on-disk format %d\n", toggledlog.ProgramName, GitVersion, contentenc.CurrentVersion)  }  func main() { @@ -142,7 +146,7 @@ func main() {  	setupColors()  	// Parse command line arguments -	flagSet = flag.NewFlagSet(cryptfs.PROGRAM_NAME, flag.ExitOnError) +	flagSet = flag.NewFlagSet(toggledlog.ProgramName, flag.ExitOnError)  	flagSet.Usage = usageText  	flagSet.BoolVar(&args.debug, "d", false, "")  	flagSet.BoolVar(&args.debug, "debug", false, "Enable debug output") @@ -168,7 +172,7 @@ func main() {  	flagSet.StringVar(&args.extpass, "extpass", "", "Use external program for the password prompt")  	flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+  		"successful mount - used internally for daemonization") -	flagSet.IntVar(&args.scryptn, "scryptn", cryptfs.SCRYPT_DEFAULT_LOGN, "scrypt cost parameter logN. "+ +	flagSet.IntVar(&args.scryptn, "scryptn", configfile.ScryptDefaultLogN, "scrypt cost parameter logN. "+  		"Setting this to a lower value speeds up mounting but makes the password susceptible to brute-force attacks")  	flagSet.Parse(os.Args[1:]) @@ -182,12 +186,12 @@ func main() {  		os.Exit(0)  	}  	if args.debug { -		cryptfs.Debug.Enabled = true -		cryptfs.Debug.Printf("Debug output enabled") +		toggledlog.Debug.Enabled = true +		toggledlog.Debug.Printf("Debug output enabled")  	}  	if args.wpanic { -		cryptfs.Warn.PanicAfter = true -		cryptfs.Debug.Printf("Panicing on warnings") +		toggledlog.Warn.PanicAfter = true +		toggledlog.Debug.Printf("Panicing on warnings")  	}  	// Every operation below requires CIPHERDIR. Check that we have it.  	if flagSet.NArg() >= 1 { @@ -203,7 +207,7 @@ func main() {  	}  	// "-q"  	if args.quiet { -		cryptfs.Info.Enabled = false +		toggledlog.Info.Enabled = false  	}  	// "-config"  	if args.config != "" { @@ -211,13 +215,13 @@ func main() {  		if err != nil {  			fmt.Printf(colorRed+"Invalid \"-config\" setting: %v\n"+colorReset, err)  		} -		cryptfs.Info.Printf("Using config file at custom location %s", args.config) +		toggledlog.Info.Printf("Using config file at custom location %s", args.config)  	} else { -		args.config = filepath.Join(args.cipherdir, cryptfs.ConfDefaultName) +		args.config = filepath.Join(args.cipherdir, configfile.ConfDefaultName)  	}  	// "-cpuprofile"  	if args.cpuprofile != "" { -		cryptfs.Info.Printf("Writing CPU profile to %s", args.cpuprofile) +		toggledlog.Info.Printf("Writing CPU profile to %s", args.cpuprofile)  		f, err := os.Create(args.cpuprofile)  		if err != nil {  			fmt.Println(err) @@ -228,7 +232,7 @@ func main() {  	}  	// "-memprofile"  	if args.memprofile != "" { -		cryptfs.Info.Printf("Writing mem profile to %s", args.memprofile) +		toggledlog.Info.Printf("Writing mem profile to %s", args.memprofile)  		f, err := os.Create(args.memprofile)  		if err != nil {  			fmt.Println(err) @@ -245,13 +249,13 @@ func main() {  	}  	// "-openssl"  	if args.openssl == false { -		cryptfs.Info.Printf("Openssl disabled") +		toggledlog.Info.Printf("Openssl disabled")  	}  	// Operation flags: init, passwd or mount  	// "-init"  	if args.init {  		if flagSet.NArg() > 1 { -			fmt.Printf("Usage: %s -init [OPTIONS] CIPHERDIR\n", cryptfs.PROGRAM_NAME) +			fmt.Printf("Usage: %s -init [OPTIONS] CIPHERDIR\n", toggledlog.ProgramName)  			os.Exit(ERREXIT_USAGE)  		}  		initDir(&args) // does not return @@ -259,7 +263,7 @@ func main() {  	// "-passwd"  	if args.passwd {  		if flagSet.NArg() > 1 { -			fmt.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR\n", cryptfs.PROGRAM_NAME) +			fmt.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR\n", toggledlog.ProgramName)  			os.Exit(ERREXIT_USAGE)  		}  		changePassword(&args) // does not return @@ -282,34 +286,34 @@ func main() {  	}  	// Get master key  	var masterkey []byte -	var confFile *cryptfs.ConfFile +	var confFile *configfile.ConfFile  	if args.masterkey != "" {  		// "-masterkey" -		cryptfs.Info.Printf("Using explicit master key.") +		toggledlog.Info.Printf("Using explicit master key.")  		masterkey = parseMasterKey(args.masterkey) -		cryptfs.Info.Printf("THE MASTER KEY IS VISIBLE VIA \"ps -auxwww\", ONLY USE THIS MODE FOR EMERGENCIES.") +		toggledlog.Info.Printf("THE MASTER KEY IS VISIBLE VIA \"ps -auxwww\", ONLY USE THIS MODE FOR EMERGENCIES.")  	} else if args.zerokey {  		// "-zerokey" -		cryptfs.Info.Printf("Using all-zero dummy master key.") -		cryptfs.Info.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.") -		masterkey = make([]byte, cryptfs.KEY_LEN) +		toggledlog.Info.Printf("Using all-zero dummy master key.") +		toggledlog.Info.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.") +		masterkey = make([]byte, cryptocore.KeyLen)  	} else {  		// Load master key from config file  		masterkey, confFile = loadConfig(&args)  		printMasterKey(masterkey)  	}  	// Initialize FUSE server -	cryptfs.Debug.Printf("cli args: %v", args) +	toggledlog.Debug.Printf("cli args: %v", args)  	srv := pathfsFrontend(masterkey, args, confFile) -	cryptfs.Info.Println(colorGreen + "Filesystem mounted and ready." + colorReset) +	toggledlog.Info.Println(colorGreen + "Filesystem mounted and ready." + colorReset)  	// We are ready - send USR1 signal to our parent and switch to syslog  	if args.notifypid > 0 {  		sendUsr1(args.notifypid)  		if !args.nosyslog { -			cryptfs.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO) -			cryptfs.Debug.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_DEBUG) -			cryptfs.Warn.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_WARNING) +			toggledlog.Info.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_INFO) +			toggledlog.Debug.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_DEBUG) +			toggledlog.Warn.SwitchToSyslog(syslog.LOG_USER | syslog.LOG_WARNING)  		}  	}  	// Wait for SIGINT in the background and unmount ourselves if we get it. @@ -322,7 +326,7 @@ func main() {  // pathfsFrontend - initialize gocryptfs/pathfs_frontend  // Calls os.Exit on errors -func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *fuse.Server { +func pathfsFrontend(key []byte, args argContainer, confFile *configfile.ConfFile) *fuse.Server {  	// Reconciliate CLI and config file arguments into a Args struct that is passed to the  	// filesystem implementation @@ -338,10 +342,10 @@ func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *  	// confFile is nil when "-zerokey" or "-masterkey" was used  	if confFile != nil {  		// Settings from the config file override command line args -		frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(cryptfs.FlagPlaintextNames) -		frontendArgs.DirIV = confFile.IsFeatureFlagSet(cryptfs.FlagDirIV) -		frontendArgs.EMENames = confFile.IsFeatureFlagSet(cryptfs.FlagEMENames) -		frontendArgs.GCMIV128 = confFile.IsFeatureFlagSet(cryptfs.FlagGCMIV128) +		frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames) +		frontendArgs.DirIV = confFile.IsFeatureFlagSet(configfile.FlagDirIV) +		frontendArgs.EMENames = confFile.IsFeatureFlagSet(configfile.FlagEMENames) +		frontendArgs.GCMIV128 = confFile.IsFeatureFlagSet(configfile.FlagGCMIV128)  	}  	// EMENames implies DirIV, both on the command line and in the config file.  	if frontendArgs.EMENames { @@ -353,7 +357,7 @@ func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *  		frontendArgs.EMENames = false  	}  	jsonBytes, _ := json.MarshalIndent(frontendArgs, "", "\t") -	cryptfs.Debug.Printf("frontendArgs: %s", string(jsonBytes)) +	toggledlog.Debug.Printf("frontendArgs: %s", string(jsonBytes))  	finalFs := pathfs_frontend.NewFS(frontendArgs)  	pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true} @@ -398,7 +402,7 @@ func handleSigint(srv *fuse.Server, mountpoint string) {  		err := srv.Unmount()  		if err != nil {  			fmt.Print(err) -			cryptfs.Info.Printf("Trying lazy unmount") +			toggledlog.Info.Printf("Trying lazy unmount")  			cmd := exec.Command("fusermount", "-u", "-z", mountpoint)  			cmd.Stdout = os.Stdout  			cmd.Stderr = os.Stderr diff --git a/masterkey.go b/masterkey.go index a9df1a8..8e28b32 100644 --- a/masterkey.go +++ b/masterkey.go @@ -3,9 +3,11 @@ package main  import (  	"encoding/hex"  	"fmt" -	"github.com/rfjakob/gocryptfs/cryptfs"  	"os"  	"strings" + +	"github.com/rfjakob/gocryptfs/internal/cryptocore" +	"github.com/rfjakob/gocryptfs/internal/toggledlog"  )  // printMasterKey - remind the user that he should store the master key in @@ -25,7 +27,7 @@ func printMasterKey(key []byte) {  		}  	} -	cryptfs.Info.Printf(` +	toggledlog.Info.Printf(`  Your master key is:      %s @@ -46,8 +48,8 @@ func parseMasterKey(masterkey string) []byte {  		fmt.Printf("Could not parse master key: %v\n", err)  		os.Exit(1)  	} -	if len(key) != cryptfs.KEY_LEN { -		fmt.Printf("Master key has length %d but we require length %d\n", len(key), cryptfs.KEY_LEN) +	if len(key) != cryptocore.KeyLen { +		fmt.Printf("Master key has length %d but we require length %d\n", len(key), cryptocore.KeyLen)  		os.Exit(1)  	}  	return key diff --git a/pathfs_frontend/file.go b/pathfs_frontend/file.go index 33ad0c7..387eb35 100644 --- a/pathfs_frontend/file.go +++ b/pathfs_frontend/file.go @@ -13,7 +13,9 @@ import (  	"github.com/hanwen/go-fuse/fuse"  	"github.com/hanwen/go-fuse/fuse/nodefs" -	"github.com/rfjakob/gocryptfs/cryptfs" + +	"github.com/rfjakob/gocryptfs/internal/contentenc" +	"github.com/rfjakob/gocryptfs/internal/toggledlog"  )  // File - based on loopbackFile in go-fuse/fuse/nodefs/files.go @@ -29,19 +31,19 @@ type file struct {  	// Was the file opened O_WRONLY?  	writeOnly bool -	// Parent CryptFS -	cfs *cryptfs.CryptFS +	// Content encryption helper +	contentEnc *contentenc.ContentEnc  	// Inode number  	ino uint64  	// File header -	header *cryptfs.FileHeader +	header *contentenc.FileHeader  	forgotten bool  } -func NewFile(fd *os.File, writeOnly bool, cfs *cryptfs.CryptFS) nodefs.File { +func NewFile(fd *os.File, writeOnly bool, contentEnc *contentenc.ContentEnc) nodefs.File {  	var st syscall.Stat_t  	syscall.Fstat(int(fd.Fd()), &st)  	wlock.register(st.Ino) @@ -49,7 +51,7 @@ func NewFile(fd *os.File, writeOnly bool, cfs *cryptfs.CryptFS) nodefs.File {  	return &file{  		fd:        fd,  		writeOnly: writeOnly, -		cfs:       cfs, +		contentEnc: contentEnc,  		ino:       st.Ino,  	}  } @@ -71,12 +73,12 @@ func (f *file) SetInode(n *nodefs.Inode) {  //  // Returns io.EOF if the file is empty  func (f *file) readHeader() error { -	buf := make([]byte, cryptfs.HEADER_LEN) +	buf := make([]byte, contentenc.HEADER_LEN)  	_, err := f.fd.ReadAt(buf, 0)  	if err != nil {  		return err  	} -	h, err := cryptfs.ParseHeader(buf) +	h, err := contentenc.ParseHeader(buf)  	if err != nil {  		return err  	} @@ -87,13 +89,13 @@ func (f *file) readHeader() error {  // createHeader - create a new random header and write it to disk  func (f *file) createHeader() error { -	h := cryptfs.RandomHeader() +	h := contentenc.RandomHeader()  	buf := h.Pack()  	// Prevent partially written (=corrupt) header by preallocating the space beforehand -	err := prealloc(int(f.fd.Fd()), 0, cryptfs.HEADER_LEN) +	err := prealloc(int(f.fd.Fd()), 0, contentenc.HEADER_LEN)  	if err != nil { -		cryptfs.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error()) +		toggledlog.Warn.Printf("ino%d: createHeader: prealloc failed: %s\n", f.ino, err.Error())  		return err  	} @@ -133,29 +135,29 @@ func (f *file) doRead(off uint64, length uint64) ([]byte, fuse.Status) {  	}  	// Read the backing ciphertext in one go -	blocks := f.cfs.ExplodePlainRange(off, length) +	blocks := f.contentEnc.ExplodePlainRange(off, length)  	alignedOffset, alignedLength := blocks[0].JointCiphertextRange(blocks)  	skip := blocks[0].Skip -	cryptfs.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d", off, length, alignedOffset, alignedLength, skip) +	toggledlog.Debug.Printf("JointCiphertextRange(%d, %d) -> %d, %d, %d", off, length, alignedOffset, alignedLength, skip)  	ciphertext := make([]byte, int(alignedLength))  	n, err := f.fd.ReadAt(ciphertext, int64(alignedOffset))  	if err != nil && err != io.EOF { -		cryptfs.Warn.Printf("read: ReadAt: %s", err.Error()) +		toggledlog.Warn.Printf("read: ReadAt: %s", err.Error())  		return nil, fuse.ToStatus(err)  	}  	// Truncate ciphertext buffer down to actually read bytes  	ciphertext = ciphertext[0:n]  	firstBlockNo := blocks[0].BlockNo -	cryptfs.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n) +	toggledlog.Debug.Printf("ReadAt offset=%d bytes (%d blocks), want=%d, got=%d", alignedOffset, firstBlockNo, alignedLength, n)  	// Decrypt it -	plaintext, err := f.cfs.DecryptBlocks(ciphertext, firstBlockNo, f.header.Id) +	plaintext, err := f.contentEnc.DecryptBlocks(ciphertext, firstBlockNo, f.header.Id)  	if err != nil { -		curruptBlockNo := firstBlockNo + f.cfs.PlainOffToBlockNo(uint64(len(plaintext))) -		cipherOff := f.cfs.BlockNoToCipherOff(curruptBlockNo) -		plainOff := f.cfs.BlockNoToPlainOff(curruptBlockNo) -		cryptfs.Warn.Printf("ino%d: doRead: corrupt block #%d (plainOff=%d, cipherOff=%d)", +		curruptBlockNo := firstBlockNo + f.contentEnc.PlainOffToBlockNo(uint64(len(plaintext))) +		cipherOff := f.contentEnc.BlockNoToCipherOff(curruptBlockNo) +		plainOff := f.contentEnc.BlockNoToPlainOff(curruptBlockNo) +		toggledlog.Warn.Printf("ino%d: doRead: corrupt block #%d (plainOff=%d, cipherOff=%d)",  			f.ino, curruptBlockNo, plainOff, cipherOff)  		return nil, fuse.EIO  	} @@ -179,23 +181,23 @@ func (f *file) Read(buf []byte, off int64) (resultData fuse.ReadResult, code fus  	f.fdLock.RLock()  	defer f.fdLock.RUnlock() -	cryptfs.Debug.Printf("ino%d: FUSE Read: offset=%d length=%d", f.ino, len(buf), off) +	toggledlog.Debug.Printf("ino%d: FUSE Read: offset=%d length=%d", f.ino, len(buf), off)  	if f.writeOnly { -		cryptfs.Warn.Printf("ino%d: Tried to read from write-only file", f.ino) +		toggledlog.Warn.Printf("ino%d: Tried to read from write-only file", f.ino)  		return nil, fuse.EBADF  	}  	out, status := f.doRead(uint64(off), uint64(len(buf)))  	if status == fuse.EIO { -		cryptfs.Warn.Printf("ino%d: Read failed with EIO, offset=%d, length=%d", f.ino, len(buf), off) +		toggledlog.Warn.Printf("ino%d: Read failed with EIO, offset=%d, length=%d", f.ino, len(buf), off)  	}  	if status != fuse.OK {  		return nil, status  	} -	cryptfs.Debug.Printf("ino%d: Read: status %v, returning %d bytes", f.ino, status, len(out)) +	toggledlog.Debug.Printf("ino%d: Read: status %v, returning %d bytes", f.ino, status, len(out))  	return fuse.ReadResultData(out), status  } @@ -225,7 +227,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {  	var written uint32  	status := fuse.OK  	dataBuf := bytes.NewBuffer(data) -	blocks := f.cfs.ExplodePlainRange(uint64(off), uint64(len(data))) +	blocks := f.contentEnc.ExplodePlainRange(uint64(off), uint64(len(data)))  	for _, b := range blocks {  		blockData := dataBuf.Next(int(b.Length)) @@ -234,26 +236,26 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {  		if b.IsPartial() {  			// Read  			o, _ := b.PlaintextRange() -			oldData, status := f.doRead(o, f.cfs.PlainBS()) +			oldData, status := f.doRead(o, f.contentEnc.PlainBS())  			if status != fuse.OK { -				cryptfs.Warn.Printf("ino%d fh%d: RMW read failed: %s", f.ino, f.intFd(), status.String()) +				toggledlog.Warn.Printf("ino%d fh%d: RMW read failed: %s", f.ino, f.intFd(), status.String())  				return written, status  			}  			// Modify -			blockData = f.cfs.MergeBlocks(oldData, blockData, int(b.Skip)) -			cryptfs.Debug.Printf("len(oldData)=%d len(blockData)=%d", len(oldData), len(blockData)) +			blockData = f.contentEnc.MergeBlocks(oldData, blockData, int(b.Skip)) +			toggledlog.Debug.Printf("len(oldData)=%d len(blockData)=%d", len(oldData), len(blockData))  		}  		// Encrypt  		blockOffset, blockLen := b.CiphertextRange() -		blockData = f.cfs.EncryptBlock(blockData, b.BlockNo, f.header.Id) -		cryptfs.Debug.Printf("ino%d: Writing %d bytes to block #%d", -			f.ino, uint64(len(blockData))-f.cfs.BlockOverhead(), b.BlockNo) +		blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.Id) +		toggledlog.Debug.Printf("ino%d: Writing %d bytes to block #%d", +			f.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo)  		// Prevent partially written (=corrupt) blocks by preallocating the space beforehand  		err := prealloc(int(f.fd.Fd()), int64(blockOffset), int64(blockLen))  		if err != nil { -			cryptfs.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %s", f.ino, f.intFd(), err.Error()) +			toggledlog.Warn.Printf("ino%d fh%d: doWrite: prealloc failed: %s", f.ino, f.intFd(), err.Error())  			status = fuse.ToStatus(err)  			break  		} @@ -262,7 +264,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {  		_, err = f.fd.WriteAt(blockData, int64(blockOffset))  		if err != nil { -			cryptfs.Warn.Printf("doWrite: Write failed: %s", err.Error()) +			toggledlog.Warn.Printf("doWrite: Write failed: %s", err.Error())  			status = fuse.ToStatus(err)  			break  		} @@ -278,18 +280,18 @@ func (f *file) Write(data []byte, off int64) (uint32, fuse.Status) {  	wlock.lock(f.ino)  	defer wlock.unlock(f.ino) -	cryptfs.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d", f.ino, off, len(data)) +	toggledlog.Debug.Printf("ino%d: FUSE Write: offset=%d length=%d", f.ino, off, len(data))  	fi, err := f.fd.Stat()  	if err != nil { -		cryptfs.Warn.Printf("Write: Fstat failed: %v", err) +		toggledlog.Warn.Printf("Write: Fstat failed: %v", err)  		return 0, fuse.ToStatus(err)  	} -	plainSize := f.cfs.CipherSizeToPlainSize(uint64(fi.Size())) +	plainSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size()))  	if f.createsHole(plainSize, off) {  		status := f.zeroPad(plainSize)  		if status != fuse.OK { -			cryptfs.Warn.Printf("zeroPad returned error %v", status) +			toggledlog.Warn.Printf("zeroPad returned error %v", status)  			return 0, status  		}  	} @@ -337,14 +339,14 @@ func (f *file) Truncate(newSize uint64) fuse.Status {  	defer wlock.unlock(f.ino)  	if f.forgotten { -		cryptfs.Warn.Printf("ino%d fh%d: Truncate on forgotten file", f.ino, f.intFd()) +		toggledlog.Warn.Printf("ino%d fh%d: Truncate on forgotten file", f.ino, f.intFd())  	}  	// Common case first: Truncate to zero  	if newSize == 0 {  		err := syscall.Ftruncate(int(f.fd.Fd()), 0)  		if err != nil { -			cryptfs.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.ino, f.intFd(), err) +			toggledlog.Warn.Printf("ino%d fh%d: Ftruncate(fd, 0) returned error: %v", f.ino, f.intFd(), err)  			return fuse.ToStatus(err)  		}  		// Truncate to zero kills the file header @@ -356,14 +358,14 @@ func (f *file) Truncate(newSize uint64) fuse.Status {  	// the file  	fi, err := f.fd.Stat()  	if err != nil { -		cryptfs.Warn.Printf("ino%d fh%d: Truncate: Fstat failed: %v", f.ino, f.intFd(), err) +		toggledlog.Warn.Printf("ino%d fh%d: Truncate: Fstat failed: %v", f.ino, f.intFd(), err)  		return fuse.ToStatus(err)  	} -	oldSize := f.cfs.CipherSizeToPlainSize(uint64(fi.Size())) +	oldSize := f.contentEnc.CipherSizeToPlainSize(uint64(fi.Size()))  	{ -		oldB := float32(oldSize) / float32(f.cfs.PlainBS()) -		newB := float32(newSize) / float32(f.cfs.PlainBS()) -		cryptfs.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize) +		oldB := float32(oldSize) / float32(f.contentEnc.PlainBS()) +		newB := float32(newSize) / float32(f.contentEnc.PlainBS()) +		toggledlog.Debug.Printf("ino%d: FUSE Truncate from %.2f to %.2f blocks (%d to %d bytes)", f.ino, oldB, newB, oldSize, newSize)  	}  	// File size stays the same - nothing to do @@ -382,7 +384,7 @@ func (f *file) Truncate(newSize uint64) fuse.Status {  			}  		} -		blocks := f.cfs.ExplodePlainRange(oldSize, newSize-oldSize) +		blocks := f.contentEnc.ExplodePlainRange(oldSize, newSize-oldSize)  		for _, b := range blocks {  			// First and last block may be partial  			if b.IsPartial() { @@ -396,7 +398,7 @@ func (f *file) Truncate(newSize uint64) fuse.Status {  				off, length := b.CiphertextRange()  				err := syscall.Ftruncate(int(f.fd.Fd()), int64(off+length))  				if err != nil { -					cryptfs.Warn.Printf("grow Ftruncate returned error: %v", err) +					toggledlog.Warn.Printf("grow Ftruncate returned error: %v", err)  					return fuse.ToStatus(err)  				}  			} @@ -404,23 +406,23 @@ func (f *file) Truncate(newSize uint64) fuse.Status {  		return fuse.OK  	} else {  		// File shrinks -		blockNo := f.cfs.PlainOffToBlockNo(newSize) -		cipherOff := f.cfs.BlockNoToCipherOff(blockNo) -		plainOff := f.cfs.BlockNoToPlainOff(blockNo) +		blockNo := f.contentEnc.PlainOffToBlockNo(newSize) +		cipherOff := f.contentEnc.BlockNoToCipherOff(blockNo) +		plainOff := f.contentEnc.BlockNoToPlainOff(blockNo)  		lastBlockLen := newSize - plainOff  		var data []byte  		if lastBlockLen > 0 {  			var status fuse.Status  			data, status = f.doRead(plainOff, lastBlockLen)  			if status != fuse.OK { -				cryptfs.Warn.Printf("shrink doRead returned error: %v", err) +				toggledlog.Warn.Printf("shrink doRead returned error: %v", err)  				return status  			}  		}  		// Truncate down to last complete block  		err = syscall.Ftruncate(int(f.fd.Fd()), int64(cipherOff))  		if err != nil { -			cryptfs.Warn.Printf("shrink Ftruncate returned error: %v", err) +			toggledlog.Warn.Printf("shrink Ftruncate returned error: %v", err)  			return fuse.ToStatus(err)  		}  		// Append partial block @@ -450,14 +452,14 @@ func (f *file) GetAttr(a *fuse.Attr) fuse.Status {  	f.fdLock.RLock()  	defer f.fdLock.RUnlock() -	cryptfs.Debug.Printf("file.GetAttr()") +	toggledlog.Debug.Printf("file.GetAttr()")  	st := syscall.Stat_t{}  	err := syscall.Fstat(int(f.fd.Fd()), &st)  	if err != nil {  		return fuse.ToStatus(err)  	}  	a.FromStat(&st) -	a.Size = f.cfs.CipherSizeToPlainSize(a.Size) +	a.Size = f.contentEnc.CipherSizeToPlainSize(a.Size)  	return fuse.OK  } @@ -468,7 +470,7 @@ var allocateWarned bool  func (f *file) Allocate(off uint64, sz uint64, mode uint32) fuse.Status {  	// Only warn once  	if !allocateWarned { -		cryptfs.Warn.Printf("fallocate(2) is not supported, returning ENOSYS - see https://github.com/rfjakob/gocryptfs/issues/1") +		toggledlog.Warn.Printf("fallocate(2) is not supported, returning ENOSYS - see https://github.com/rfjakob/gocryptfs/issues/1")  		allocateWarned = true  	}  	return fuse.ENOSYS diff --git a/pathfs_frontend/file_holes.go b/pathfs_frontend/file_holes.go index fd2e2c1..a147deb 100644 --- a/pathfs_frontend/file_holes.go +++ b/pathfs_frontend/file_holes.go @@ -4,13 +4,14 @@ package pathfs_frontend  import (  	"github.com/hanwen/go-fuse/fuse" -	"github.com/rfjakob/gocryptfs/cryptfs" + +	"github.com/rfjakob/gocryptfs/internal/toggledlog"  )  // Will a write to offset "off" create a file hole?  func (f *file) createsHole(plainSize uint64, off int64) bool { -	nextBlock := f.cfs.PlainOffToBlockNo(plainSize) -	targetBlock := f.cfs.PlainOffToBlockNo(uint64(off)) +	nextBlock := f.contentEnc.PlainOffToBlockNo(plainSize) +	targetBlock := f.contentEnc.PlainOffToBlockNo(uint64(off))  	if targetBlock > nextBlock {  		return true  	} @@ -19,10 +20,10 @@ func (f *file) createsHole(plainSize uint64, off int64) bool {  // Zero-pad the file of size plainSize to the next block boundary  func (f *file) zeroPad(plainSize uint64) fuse.Status { -	lastBlockLen := plainSize % f.cfs.PlainBS() -	missing := f.cfs.PlainBS() - lastBlockLen +	lastBlockLen := plainSize % f.contentEnc.PlainBS() +	missing := f.contentEnc.PlainBS() - lastBlockLen  	pad := make([]byte, missing) -	cryptfs.Debug.Printf("zeroPad: Writing %d bytes\n", missing) +	toggledlog.Debug.Printf("zeroPad: Writing %d bytes\n", missing)  	_, status := f.doWrite(pad, int64(plainSize))  	return status  } diff --git a/pathfs_frontend/fs.go b/pathfs_frontend/fs.go index a00985d..212f0a7 100644 --- a/pathfs_frontend/fs.go +++ b/pathfs_frontend/fs.go @@ -13,25 +13,41 @@ import (  	"github.com/hanwen/go-fuse/fuse"  	"github.com/hanwen/go-fuse/fuse/nodefs"  	"github.com/hanwen/go-fuse/fuse/pathfs" -	"github.com/rfjakob/gocryptfs/cryptfs" + +	"github.com/rfjakob/gocryptfs/internal/toggledlog" +	"github.com/rfjakob/gocryptfs/internal/cryptocore" +	"github.com/rfjakob/gocryptfs/internal/nametransform" +	"github.com/rfjakob/gocryptfs/internal/contentenc" +	"github.com/rfjakob/gocryptfs/internal/configfile"  ) +const plainBS = 4096 +  type FS struct { -	*cryptfs.CryptFS  	pathfs.FileSystem      // loopbackFileSystem, see go-fuse/fuse/pathfs/loopback.go  	args              Args // Stores configuration arguments  	// dirIVLock: Lock()ed if any "gocryptfs.diriv" file is modified  	// Readers must RLock() it to prevent them from seeing intermediate  	// states  	dirIVLock sync.RWMutex +	// Filename encryption helper +	nameTransform *nametransform.NameTransform +	// Content encryption helper +	contentEnc *contentenc.ContentEnc  }  // Encrypted FUSE overlay filesystem  func NewFS(args Args) *FS { + +	cryptoCore := cryptocore.New(args.Masterkey, args.OpenSSL, args.GCMIV128) +	contentEnc := contentenc.New(cryptoCore, plainBS) +	nameTransform := nametransform.New(cryptoCore, args.EMENames) +  	return &FS{ -		CryptFS:    cryptfs.NewCryptFS(args.Masterkey, args.OpenSSL, args.PlaintextNames, args.GCMIV128),  		FileSystem: pathfs.NewLoopbackFileSystem(args.Cipherdir),  		args:       args, +		nameTransform: nameTransform, +		contentEnc: contentEnc,  	}  } @@ -43,12 +59,12 @@ func (fs *FS) getBackingPath(relPath string) (string, error) {  		return "", err  	}  	cAbsPath := filepath.Join(fs.args.Cipherdir, cPath) -	cryptfs.Debug.Printf("getBackingPath: %s + %s -> %s", fs.args.Cipherdir, relPath, cAbsPath) +	toggledlog.Debug.Printf("getBackingPath: %s + %s -> %s", fs.args.Cipherdir, relPath, cAbsPath)  	return cAbsPath, nil  }  func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Status) { -	cryptfs.Debug.Printf("FS.GetAttr('%s')", name) +	toggledlog.Debug.Printf("FS.GetAttr('%s')", name)  	if fs.isFiltered(name) {  		return nil, fuse.EPERM  	} @@ -58,11 +74,11 @@ func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Stat  	}  	a, status := fs.FileSystem.GetAttr(cName, context)  	if a == nil { -		cryptfs.Debug.Printf("FS.GetAttr failed: %s", status.String()) +		toggledlog.Debug.Printf("FS.GetAttr failed: %s", status.String())  		return a, status  	}  	if a.IsRegular() { -		a.Size = fs.CipherSizeToPlainSize(a.Size) +		a.Size = fs.contentEnc.CipherSizeToPlainSize(a.Size)  	} else if a.IsSymlink() {  		target, _ := fs.Readlink(name, context)  		a.Size = uint64(len(target)) @@ -71,7 +87,7 @@ func (fs *FS) GetAttr(name string, context *fuse.Context) (*fuse.Attr, fuse.Stat  }  func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, fuse.Status) { -	cryptfs.Debug.Printf("OpenDir(%s)", dirName) +	toggledlog.Debug.Printf("OpenDir(%s)", dirName)  	cDirName, err := fs.encryptPath(dirName)  	if err != nil {  		return nil, fuse.ToStatus(err) @@ -81,12 +97,12 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f  	if cipherEntries == nil {  		return nil, status  	} -	// Get DirIV (stays zero if DirIV if off) -	cachedIV := make([]byte, cryptfs.DIRIV_LEN) +	// Get DirIV (stays nil if DirIV if off) +	var cachedIV []byte  	if fs.args.DirIV {  		// Read the DirIV once and use it for all later name decryptions  		cDirAbsPath := filepath.Join(fs.args.Cipherdir, cDirName) -		cachedIV, err = fs.CryptFS.ReadDirIV(cDirAbsPath) +		cachedIV, err = fs.nameTransform.ReadDirIV(cDirAbsPath)  		if err != nil {  			return nil, fuse.ToStatus(err)  		} @@ -95,19 +111,19 @@ func (fs *FS) OpenDir(dirName string, context *fuse.Context) ([]fuse.DirEntry, f  	var plain []fuse.DirEntry  	for i := range cipherEntries {  		cName := cipherEntries[i].Name -		if dirName == "" && cName == cryptfs.ConfDefaultName { +		if dirName == "" && cName == configfile.ConfDefaultName {  			// silently ignore "gocryptfs.conf" in the top level dir  			continue  		} -		if fs.args.DirIV && cName == cryptfs.DIRIV_FILENAME { +		if fs.args.DirIV && cName == nametransform.DirIVFilename {  			// silently ignore "gocryptfs.diriv" everywhere if dirIV is enabled  			continue  		}  		var name string = cName  		if !fs.args.PlaintextNames { -			name, err = fs.CryptFS.DecryptName(cName, cachedIV, fs.args.EMENames) +			name, err = fs.nameTransform.DecryptName(cName, cachedIV)  			if err != nil { -				cryptfs.Warn.Printf("Invalid name \"%s\" in dir \"%s\": %s", cName, cDirName, err) +				toggledlog.Warn.Printf("Invalid name \"%s\" in dir \"%s\": %s", cName, cDirName, err)  				continue  			}  		} @@ -137,16 +153,16 @@ func (fs *FS) Open(path string, flags uint32, context *fuse.Context) (fuseFile n  	iflags, writeOnly := fs.mangleOpenFlags(flags)  	cPath, err := fs.getBackingPath(path)  	if err != nil { -		cryptfs.Debug.Printf("Open: getBackingPath: %v", err) +		toggledlog.Debug.Printf("Open: getBackingPath: %v", err)  		return nil, fuse.ToStatus(err)  	} -	cryptfs.Debug.Printf("Open: %s", cPath) +	toggledlog.Debug.Printf("Open: %s", cPath)  	f, err := os.OpenFile(cPath, iflags, 0666)  	if err != nil {  		return nil, fuse.ToStatus(err)  	} -	return NewFile(f, writeOnly, fs.CryptFS), fuse.OK +	return NewFile(f, writeOnly, fs.contentEnc), fuse.OK  }  func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Context) (fuseFile nodefs.File, code fuse.Status) { @@ -162,7 +178,7 @@ func (fs *FS) Create(path string, flags uint32, mode uint32, context *fuse.Conte  	if err != nil {  		return nil, fuse.ToStatus(err)  	} -	return NewFile(f, writeOnly, fs.CryptFS), fuse.OK +	return NewFile(f, writeOnly, fs.contentEnc), fuse.OK  }  func (fs *FS) Chmod(path string, mode uint32, context *fuse.Context) (code fuse.Status) { @@ -203,7 +219,7 @@ var truncateWarned bool  func (fs *FS) Truncate(path string, offset uint64, context *fuse.Context) (code fuse.Status) {  	// Only warn once  	if !truncateWarned { -		cryptfs.Warn.Printf("truncate(2) is not supported, returning ENOSYS - use ftruncate(2)") +		toggledlog.Warn.Printf("truncate(2) is not supported, returning ENOSYS - use ftruncate(2)")  		truncateWarned = true  	}  	return fuse.ENOSYS @@ -233,7 +249,7 @@ func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status f  	if !fs.args.DirIV {  		target, err := fs.decryptPath(cTarget)  		if err != nil { -			cryptfs.Warn.Printf("Readlink: CBC decryption failed: %v", err) +			toggledlog.Warn.Printf("Readlink: CBC decryption failed: %v", err)  			return "", fuse.EIO  		}  		return target, fuse.OK @@ -241,12 +257,12 @@ func (fs *FS) Readlink(path string, context *fuse.Context) (out string, status f  	// Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM)  	cBinTarget, err := base64.URLEncoding.DecodeString(cTarget)  	if err != nil { -		cryptfs.Warn.Printf("Readlink: %v", err) +		toggledlog.Warn.Printf("Readlink: %v", err)  		return "", fuse.EIO  	} -	target, err := fs.CryptFS.DecryptBlock([]byte(cBinTarget), 0, nil) +	target, err := fs.contentEnc.DecryptBlock([]byte(cBinTarget), 0, nil)  	if err != nil { -		cryptfs.Warn.Printf("Readlink: %v", err) +		toggledlog.Warn.Printf("Readlink: %v", err)  		return "", fuse.EIO  	}  	return string(target), fuse.OK @@ -264,7 +280,7 @@ func (fs *FS) Unlink(path string, context *fuse.Context) (code fuse.Status) {  }  func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (code fuse.Status) { -	cryptfs.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName) +	toggledlog.Debug.Printf("Symlink(\"%s\", \"%s\")", target, linkName)  	if fs.isFiltered(linkName) {  		return fuse.EPERM  	} @@ -276,18 +292,18 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co  	if !fs.args.DirIV {  		cTarget, err := fs.encryptPath(target)  		if err != nil { -			cryptfs.Warn.Printf("Symlink: BUG: we should not get an error here: %v", err) +			toggledlog.Warn.Printf("Symlink: BUG: we should not get an error here: %v", err)  			return fuse.ToStatus(err)  		}  		err = os.Symlink(cTarget, cPath)  		return fuse.ToStatus(err)  	}  	// Since gocryptfs v0.5 symlinks are encrypted like file contents (GCM) -	cBinTarget := fs.CryptFS.EncryptBlock([]byte(target), 0, nil) +	cBinTarget := fs.contentEnc.EncryptBlock([]byte(target), 0, nil)  	cTarget := base64.URLEncoding.EncodeToString(cBinTarget)  	err = os.Symlink(cTarget, cPath) -	cryptfs.Debug.Printf("Symlink: os.Symlink(%s, %s) = %v", cTarget, cPath, err) +	toggledlog.Debug.Printf("Symlink: os.Symlink(%s, %s) = %v", cTarget, cPath, err)  	return fuse.ToStatus(err)  } @@ -305,7 +321,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod  	}  	// The Rename may cause a directory to take the place of another directory.  	// That directory may still be in the DirIV cache, clear it. -	fs.CryptFS.DirIVCache.Clear() +	fs.nameTransform.DirIVCache.Clear()  	err = os.Rename(cOldPath, cNewPath) @@ -313,7 +329,7 @@ func (fs *FS) Rename(oldPath string, newPath string, context *fuse.Context) (cod  		// If an empty directory is overwritten we will always get  		// ENOTEMPTY as the "empty" directory will still contain gocryptfs.diriv.  		// Handle that case by removing the target directory and trying again. -		cryptfs.Debug.Printf("Rename: Handling ENOTEMPTY") +		toggledlog.Debug.Printf("Rename: Handling ENOTEMPTY")  		if fs.Rmdir(newPath, context) == fuse.OK {  			err = os.Rename(cOldPath, cNewPath)  		} diff --git a/pathfs_frontend/fs_dir.go b/pathfs_frontend/fs_dir.go index b1edd73..d378d28 100644 --- a/pathfs_frontend/fs_dir.go +++ b/pathfs_frontend/fs_dir.go @@ -9,7 +9,10 @@ import (  	"syscall"  	"github.com/hanwen/go-fuse/fuse" -	"github.com/rfjakob/gocryptfs/cryptfs" + +	"github.com/rfjakob/gocryptfs/internal/toggledlog" +	"github.com/rfjakob/gocryptfs/internal/cryptocore" +	"github.com/rfjakob/gocryptfs/internal/nametransform"  )  func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fuse.Status) { @@ -29,7 +32,7 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu  	mode = mode | 0300  	// The new directory may take the place of an older one that is still in the cache -	fs.CryptFS.DirIVCache.Clear() +	fs.nameTransform.DirIVCache.Clear()  	// Create directory  	fs.dirIVLock.Lock()  	defer fs.dirIVLock.Unlock() @@ -38,13 +41,13 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu  		return fuse.ToStatus(err)  	}  	// Create gocryptfs.diriv inside -	err = cryptfs.WriteDirIV(encPath) +	err = nametransform.WriteDirIV(encPath)  	if err != nil {  		// This should not happen -		cryptfs.Warn.Printf("Mkdir: WriteDirIV failed: %v", err) +		toggledlog.Warn.Printf("Mkdir: WriteDirIV failed: %v", err)  		err2 := syscall.Rmdir(encPath)  		if err2 != nil { -			cryptfs.Warn.Printf("Mkdir: Rmdir rollback failed: %v", err2) +			toggledlog.Warn.Printf("Mkdir: Rmdir rollback failed: %v", err2)  		}  		return fuse.ToStatus(err)  	} @@ -53,7 +56,7 @@ func (fs *FS) Mkdir(relPath string, mode uint32, context *fuse.Context) (code fu  	if origMode != mode {  		err = os.Chmod(encPath, os.FileMode(origMode))  		if err != nil { -			cryptfs.Warn.Printf("Mkdir: Chmod failed: %v", err) +			toggledlog.Warn.Printf("Mkdir: Chmod failed: %v", err)  		}  	} @@ -74,17 +77,17 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {  	fd, err := os.Open(encPath)  	if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.EACCES {  		// We need permission to read and modify the directory -		cryptfs.Debug.Printf("Rmdir: handling EACCESS") +		toggledlog.Debug.Printf("Rmdir: handling EACCESS")  		fi, err2 := os.Stat(encPath)  		if err2 != nil { -			cryptfs.Debug.Printf("Rmdir: Stat: %v", err2) +			toggledlog.Debug.Printf("Rmdir: Stat: %v", err2)  			return fuse.ToStatus(err2)  		}  		origMode := fi.Mode()  		newMode := origMode | 0700  		err2 = os.Chmod(encPath, newMode)  		if err2 != nil { -			cryptfs.Debug.Printf("Rmdir: Chmod failed: %v", err2) +			toggledlog.Debug.Printf("Rmdir: Chmod failed: %v", err2)  			return fuse.ToStatus(err)  		}  		defer func() { @@ -92,7 +95,7 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {  				// Undo the chmod if removing the directory failed  				err3 := os.Chmod(encPath, origMode)  				if err3 != nil { -					cryptfs.Warn.Printf("Rmdir: Chmod rollback failed: %v", err2) +					toggledlog.Warn.Printf("Rmdir: Chmod rollback failed: %v", err2)  				}  			}  		}() @@ -100,35 +103,36 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {  		fd, err = os.Open(encPath)  	}  	if err != nil { -		cryptfs.Debug.Printf("Rmdir: Open: %v", err) +		toggledlog.Debug.Printf("Rmdir: Open: %v", err)  		return fuse.ToStatus(err)  	}  	list, err := fd.Readdirnames(10)  	fd.Close()  	if err != nil { -		cryptfs.Debug.Printf("Rmdir: Readdirnames: %v", err) +		toggledlog.Debug.Printf("Rmdir: Readdirnames: %v", err)  		return fuse.ToStatus(err)  	}  	if len(list) > 1 {  		return fuse.ToStatus(syscall.ENOTEMPTY)  	} else if len(list) == 0 { -		cryptfs.Warn.Printf("Rmdir: gocryptfs.diriv missing, allowing deletion") +		toggledlog.Warn.Printf("Rmdir: gocryptfs.diriv missing, allowing deletion")  		return fuse.ToStatus(syscall.Rmdir(encPath))  	}  	// Move "gocryptfs.diriv" to the parent dir as "gocryptfs.diriv.rmdir.XYZ" -	dirivPath := filepath.Join(encPath, cryptfs.DIRIV_FILENAME) +	dirivPath := filepath.Join(encPath, nametransform.DirIVFilename)  	parentDir := filepath.Dir(encPath) -	tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptfs.RandUint64()) +	tmpName := fmt.Sprintf("gocryptfs.diriv.rmdir.%d", cryptocore.RandUint64())  	tmpDirivPath := filepath.Join(parentDir, tmpName) -	cryptfs.Debug.Printf("Rmdir: Renaming %s to %s", cryptfs.DIRIV_FILENAME, tmpDirivPath) +	toggledlog.Debug.Printf("Rmdir: Renaming %s to %s", nametransform.DirIVFilename, tmpDirivPath)  	// The directory is in an inconsistent state between rename and rmdir. Protect against  	// concurrent readers.  	fs.dirIVLock.Lock()  	defer fs.dirIVLock.Unlock()  	err = os.Rename(dirivPath, tmpDirivPath)  	if err != nil { -		cryptfs.Warn.Printf("Rmdir: Renaming %s to %s failed: %v", cryptfs.DIRIV_FILENAME, tmpDirivPath, err) +		toggledlog.Warn.Printf("Rmdir: Renaming %s to %s failed: %v", +			nametransform.DirIVFilename, tmpDirivPath, err)  		return fuse.ToStatus(err)  	}  	// Actual Rmdir @@ -138,16 +142,16 @@ func (fs *FS) Rmdir(name string, context *fuse.Context) (code fuse.Status) {  		// meantime, undo the rename  		err2 := os.Rename(tmpDirivPath, dirivPath)  		if err2 != nil { -			cryptfs.Warn.Printf("Rmdir: Rename rollback failed: %v", err2) +			toggledlog.Warn.Printf("Rmdir: Rename rollback failed: %v", err2)  		}  		return fuse.ToStatus(err)  	}  	// Delete "gocryptfs.diriv.rmdir.INODENUMBER"  	err = syscall.Unlink(tmpDirivPath)  	if err != nil { -		cryptfs.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err) +		toggledlog.Warn.Printf("Rmdir: Could not clean up %s: %v", tmpName, err)  	}  	// The now-deleted directory may have been in the DirIV cache. Clear it. -	fs.CryptFS.DirIVCache.Clear() +	fs.nameTransform.DirIVCache.Clear()  	return fuse.OK  } diff --git a/pathfs_frontend/names.go b/pathfs_frontend/names.go index bd7a249..160fa0a 100644 --- a/pathfs_frontend/names.go +++ b/pathfs_frontend/names.go @@ -3,7 +3,8 @@ package pathfs_frontend  // This file forwards file encryption operations to cryptfs  import ( -	"github.com/rfjakob/gocryptfs/cryptfs" +	"github.com/rfjakob/gocryptfs/internal/configfile" +	mylog "github.com/rfjakob/gocryptfs/internal/toggledlog"  )  // isFiltered - check if plaintext "path" should be forbidden @@ -14,9 +15,9 @@ func (fs *FS) isFiltered(path string) bool {  		return false  	}  	// gocryptfs.conf in the root directory is forbidden -	if path == cryptfs.ConfDefaultName { -		cryptfs.Info.Printf("The name /%s is reserved when -plaintextnames is used\n", -			cryptfs.ConfDefaultName) +	if path == configfile.ConfDefaultName { +		mylog.Info.Printf("The name /%s is reserved when -plaintextnames is used\n", +			configfile.ConfDefaultName)  		return true  	}  	// Note: gocryptfs.diriv is NOT forbidden because diriv and plaintextnames @@ -30,11 +31,11 @@ func (fs *FS) encryptPath(plainPath string) (string, error) {  		return plainPath, nil  	}  	if !fs.args.DirIV { -		return fs.CryptFS.EncryptPathNoIV(plainPath), nil +		return fs.nameTransform.EncryptPathNoIV(plainPath), nil  	}  	fs.dirIVLock.RLock()  	defer fs.dirIVLock.RUnlock() -	return fs.CryptFS.EncryptPathDirIV(plainPath, fs.args.Cipherdir, fs.args.EMENames) +	return fs.nameTransform.EncryptPathDirIV(plainPath, fs.args.Cipherdir)  }  // decryptPath - decrypt relative ciphertext path @@ -43,9 +44,9 @@ func (fs *FS) decryptPath(cipherPath string) (string, error) {  		return cipherPath, nil  	}  	if !fs.args.DirIV { -		return fs.CryptFS.DecryptPathNoIV(cipherPath) +		return fs.nameTransform.DecryptPathNoIV(cipherPath)  	}  	fs.dirIVLock.RLock()  	defer fs.dirIVLock.RUnlock() -	return fs.CryptFS.DecryptPathDirIV(cipherPath, fs.args.Cipherdir, fs.args.EMENames) +	return fs.nameTransform.DecryptPathDirIV(cipherPath, fs.args.Cipherdir, fs.args.EMENames)  } @@ -4,7 +4,7 @@ set -eu  cd "$(dirname "$0")" -go test ./cryptfs $* +#go test ./cryptfs $*  source build.bash  go test ./integration_tests $* | 
