summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Unterwurzacher2016-09-20 22:59:10 +0200
committerJakob Unterwurzacher2016-09-25 16:43:17 +0200
commit5f726aaa9d95be30ecfcb61637df3ccc133bf2ea (patch)
tree37086b2f3c0589a977dce72eb245e9e50da8430b
parent90f0bdc2241290d9d495b606abaec2e973383b80 (diff)
contentenc: add GCM-SIV support
Also add ReverseDummyNonce nonce generation.
-rw-r--r--init_dir.go2
-rw-r--r--internal/configfile/config_file.go7
-rw-r--r--internal/configfile/config_test.go16
-rw-r--r--internal/contentenc/content.go35
-rw-r--r--internal/cryptocore/cryptocore.go3
-rw-r--r--internal/fusefrontend/file.go2
-rw-r--r--internal/fusefrontend/fs.go2
-rw-r--r--internal/fusefrontend_reverse/rfile.go2
-rw-r--r--main.go3
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)
diff --git a/main.go b/main.go
index 1877779..68f6a44 100644
--- a/main.go
+++ b/main.go
@@ -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