diff options
| -rw-r--r-- | init_dir.go | 2 | ||||
| -rw-r--r-- | internal/configfile/config_file.go | 7 | ||||
| -rw-r--r-- | internal/configfile/config_test.go | 16 | ||||
| -rw-r--r-- | internal/contentenc/content.go | 35 | ||||
| -rw-r--r-- | internal/cryptocore/cryptocore.go | 3 | ||||
| -rw-r--r-- | internal/fusefrontend/file.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend/fs.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rfile.go | 2 | ||||
| -rw-r--r-- | main.go | 3 | 
9 files changed, 59 insertions, 13 deletions
| diff --git a/init_dir.go b/init_dir.go index c52e2fa..fac4053 100644 --- a/init_dir.go +++ b/init_dir.go @@ -39,7 +39,7 @@ func initDir(args *argContainer) {  	}  	password := readpassword.Twice(args.extpass)  	creator := tlog.ProgramName + " " + GitVersion -	err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator) +	err = configfile.CreateConfFile(args.config, password, args.plaintextnames, args.scryptn, creator, args.reverse)  	if err != nil {  		tlog.Fatal.Println(err)  		os.Exit(ERREXIT_INIT) diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index 178890b..b1504b4 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -45,7 +45,7 @@ type ConfFile struct {  // CreateConfFile - create a new config with a random key encrypted with  // "password" and write it to "filename".  // Uses scrypt with cost parameter logN. -func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string) error { +func CreateConfFile(filename string, password string, plaintextNames bool, logN int, creator string, reverse bool) error {  	var cf ConfFile  	cf.filename = filename  	cf.Creator = creator @@ -67,6 +67,9 @@ func CreateConfFile(filename string, password string, plaintextNames bool, logN  		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])  		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])  	} +	if reverse { +		cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMSIV]) +	}  	// Write file to disk  	return cf.WriteFile() @@ -165,7 +168,7 @@ func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {  	// Lock master key using password-based key  	cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, 96)  	ce := contentenc.New(cc, 4096) -	cf.EncryptedKey = ce.EncryptBlock(key, 0, nil) +	cf.EncryptedKey = ce.EncryptBlock(key, 0, nil, contentenc.RandomNonce)  }  // WriteFile - write out config in JSON format to file "filename.tmp" diff --git a/internal/configfile/config_test.go b/internal/configfile/config_test.go index e34a3cd..72c25f6 100644 --- a/internal/configfile/config_test.go +++ b/internal/configfile/config_test.go @@ -60,7 +60,7 @@ func TestLoadV2StrangeFeature(t *testing.T) {  }  func TestCreateConfFile(t *testing.T) { -	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test") +	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", false)  	if err != nil {  		t.Fatal(err)  	} @@ -71,6 +71,20 @@ func TestCreateConfFile(t *testing.T) {  } +func TestCreateConfFileReverse(t *testing.T) { +	err := CreateConfFile("config_test/tmp.conf", "test", false, 10, "test", true) +	if err != nil { +		t.Fatal(err) +	} +	_, c, err := LoadConfFile("config_test/tmp.conf", "test") +	if err != nil { +		t.Fatal(err) +	} +	if !c.IsFeatureFlagSet(FlagGCMSIV) { +		t.Error("GCMSIV flag should be set but is not") +	} +} +  func TestIsFeatureFlagKnown(t *testing.T) {  	// Test a few hardcoded values  	testKnownFlags := []string{"DirIV", "PlaintextNames", "EMENames", "GCMIV128", "LongNames"} diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index e132536..c638221 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -11,11 +11,17 @@ import (  	"github.com/rfjakob/gocryptfs/internal/tlog"  ) +type NonceMode int +  const (  	// Default plaintext block size  	DefaultBS = 4096  	// We always use 128-bit IVs for file content encryption  	IVBitLen = 128 + +	_                           = iota // skip zero +	RandomNonce       NonceMode = iota +	ReverseDummyNonce NonceMode = iota  )  type ContentEnc struct { @@ -27,6 +33,8 @@ type ContentEnc struct {  	cipherBS uint64  	// All-zero block of size cipherBS, for fast compares  	allZeroBlock []byte +	// All-zero block of size IVBitLen/8, for fast compares +	allZeroNonce []byte  }  func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc { @@ -38,6 +46,7 @@ func New(cc *cryptocore.CryptoCore, plainBS uint64) *ContentEnc {  		plainBS:      plainBS,  		cipherBS:     cipherBS,  		allZeroBlock: make([]byte, cipherBS), +		allZeroNonce: make([]byte, IVBitLen/8),  	}  } @@ -94,6 +103,9 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b  	// Extract nonce  	nonce := ciphertext[:be.cryptoCore.IVLen] +	if bytes.Equal(nonce, be.allZeroNonce) && be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV { +		panic("Hit an all-zero nonce with GCMSIV off. This MUST NOT happen!") +	}  	ciphertextOrig := ciphertext  	ciphertext = ciphertext[be.cryptoCore.IVLen:] @@ -115,27 +127,38 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b  // EncryptBlocks - Encrypt a number of blocks  // Used for reverse mode -func (be *ContentEnc) EncryptBlocks(plaintext []byte, firstBlockNo uint64, fileId []byte) []byte { +func (be *ContentEnc) EncryptBlocks(plaintext []byte, firstBlockNo uint64, fileId []byte, nMode NonceMode) []byte {  	inBuf := bytes.NewBuffer(plaintext)  	var outBuf bytes.Buffer  	for blockNo := firstBlockNo; inBuf.Len() > 0; blockNo++ {  		inBlock := inBuf.Next(int(be.plainBS)) -		outBlock := be.EncryptBlock(inBlock, blockNo, fileId) +		outBlock := be.EncryptBlock(inBlock, blockNo, fileId, nMode)  		outBuf.Write(outBlock)  	}  	return outBuf.Bytes()  }  // encryptBlock - Encrypt and add IV and MAC -func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte) []byte { - +func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte, nMode NonceMode) []byte {  	// Empty block?  	if len(plaintext) == 0 {  		return plaintext  	} -	// Get fresh nonce -	nonce := be.cryptoCore.IVGenerator.Get() +	var nonce []byte +	switch nMode { +	case ReverseDummyNonce: +		if be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV { +			panic("MUST NOT use dummy nonces unless in GCMSIV mode!") +		} +		nonce = make([]byte, IVBitLen/8) +		binary.BigEndian.PutUint64(nonce, blockNo) +	case RandomNonce: +		// Get a fresh random nonce +		nonce = be.cryptoCore.IVGenerator.Get() +	default: +		panic("invalid nonce mode") +	}  	// Authenticate block with block number and file ID  	aData := make([]byte, 8) diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index a6708bd..0913ed0 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -29,6 +29,8 @@ type CryptoCore struct {  	BlockCipher cipher.Block  	// GCM or GCM-SIV. This is used for content encryption.  	AEADCipher cipher.AEAD +	// Which backend is behind AEADCipher? +	AEADBackend BackendTypeEnum  	// GCM needs unique IVs (nonces)  	IVGenerator *nonceGenerator  	IVLen       int @@ -74,6 +76,7 @@ func New(key []byte, backend BackendTypeEnum, IVBitLen int) *CryptoCore {  	return &CryptoCore{  		BlockCipher: blockCipher,  		AEADCipher:  gcm, +		AEADBackend: backend,  		IVGenerator: &nonceGenerator{nonceLen: IVLen},  		IVLen:       IVLen,  	} diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go index a04b6af..b9edc76 100644 --- a/internal/fusefrontend/file.go +++ b/internal/fusefrontend/file.go @@ -256,7 +256,7 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {  		// Encrypt  		blockOffset := b.BlockCipherOff() -		blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.Id) +		blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.Id, contentenc.RandomNonce)  		tlog.Debug.Printf("ino%d: Writing %d bytes to block #%d",  			f.ino, uint64(len(blockData))-f.contentEnc.BlockOverhead(), b.BlockNo) diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index 575865e..a3db3dc 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -326,7 +326,7 @@ func (fs *FS) Symlink(target string, linkName string, context *fuse.Context) (co  		return fuse.ToStatus(err)  	}  	// Symlinks are encrypted like file contents (GCM) and base64-encoded -	cBinTarget := fs.contentEnc.EncryptBlock([]byte(target), 0, nil) +	cBinTarget := fs.contentEnc.EncryptBlock([]byte(target), 0, nil, contentenc.RandomNonce)  	cTarget := base64.URLEncoding.EncodeToString(cBinTarget)  	// Handle long file name diff --git a/internal/fusefrontend_reverse/rfile.go b/internal/fusefrontend_reverse/rfile.go index 75ebfd9..7e54b17 100644 --- a/internal/fusefrontend_reverse/rfile.go +++ b/internal/fusefrontend_reverse/rfile.go @@ -64,7 +64,7 @@ func (rf *reverseFile) readBackingFile(off uint64, length uint64) (out []byte, e  	plaintext = plaintext[0:n]  	// Encrypt blocks -	ciphertext := rf.contentEnc.EncryptBlocks(plaintext, blocks[0].BlockNo, zeroFileHeader.Id) +	ciphertext := rf.contentEnc.EncryptBlocks(plaintext, blocks[0].BlockNo, zeroFileHeader.Id, contentenc.ReverseDummyNonce)  	// Crop down to the relevant part  	lenHave := len(ciphertext) @@ -305,6 +305,9 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi  		frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames)  		if confFile.IsFeatureFlagSet(configfile.FlagGCMSIV) {  			frontendArgs.CryptoBackend = cryptocore.BackendGCMSIV +		} else if args.reverse { +			tlog.Fatal.Printf("GCMSIV is required by reverse mode, but not enabled in the config file") +			os.Exit(ERREXIT_USAGE)  		}  	}  	// If allow_other is set and we run as root, try to give newly created files to | 
