aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/configfile/config_file.go2
-rw-r--r--internal/contentenc/content.go58
-rw-r--r--internal/fusefrontend/file.go2
-rw-r--r--internal/fusefrontend/fs.go2
-rw-r--r--internal/fusefrontend_reverse/reverse_longnames.go2
-rw-r--r--internal/fusefrontend_reverse/rfile.go28
-rw-r--r--internal/fusefrontend_reverse/rfs.go6
-rw-r--r--internal/fusefrontend_reverse/rpath.go19
-rw-r--r--internal/fusefrontend_reverse/virtualfile.go2
9 files changed, 70 insertions, 51 deletions
diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go
index f58c51c..9152523 100644
--- a/internal/configfile/config_file.go
+++ b/internal/configfile/config_file.go
@@ -168,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, contentenc.RandomNonce, nil)
+ cf.EncryptedKey = ce.EncryptBlock(key, 0, nil)
}
// WriteFile - write out config in JSON format to file "filename.tmp"
diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go
index 86be7d5..ac2e8de 100644
--- a/internal/contentenc/content.go
+++ b/internal/contentenc/content.go
@@ -127,50 +127,34 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b
return plaintext, nil
}
-// EncryptBlocks - Encrypt a number of blocks
-// Used for reverse mode
-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, nMode, nil)
- outBuf.Write(outBlock)
+// EncryptBlock - Encrypt plaintext using a random nonce.
+// blockNo and fileID are used as associated data.
+// The output is nonce + ciphertext + tag.
+func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte) []byte {
+ // Get a fresh random nonce
+ nonce := be.cryptoCore.IVGenerator.Get()
+ return be.doEncryptBlock(plaintext, blockNo, fileID, nonce)
+}
+
+// EncryptBlockNonce - Encrypt plaintext using a nonce chosen by the caller.
+// blockNo and fileID are used as associated data.
+// The output is nonce + ciphertext + tag.
+// This function can only be used in SIV mode.
+func (be *ContentEnc) EncryptBlockNonce(plaintext []byte, blockNo uint64, fileID []byte, nonce []byte) []byte {
+ if be.cryptoCore.AEADBackend != cryptocore.BackendAESSIV {
+ panic("deterministic nonces are only secure in SIV mode")
}
- return outBuf.Bytes()
+ return be.doEncryptBlock(plaintext, blockNo, fileID, nonce)
}
-// encryptBlock - Encrypt and add IV and MAC
-func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []byte, nMode NonceMode, externalNonce []byte) []byte {
+// doEncryptBlock is the backend for EncryptBlock and EncryptBlockNonce.
+// blockNo and fileID are used as associated data.
+// The output is nonce + ciphertext + tag.
+func (be *ContentEnc) doEncryptBlock(plaintext []byte, blockNo uint64, fileID []byte, nonce []byte) []byte {
// Empty block?
if len(plaintext) == 0 {
return plaintext
}
-
- var nonce []byte
- switch nMode {
- case ExternalNonce:
- if be.cryptoCore.AEADBackend != cryptocore.BackendAESSIV {
- panic("MUST NOT use deterministic nonces unless in AESSIV mode!")
- }
- nonce = externalNonce
- case ReverseDeterministicNonce:
- if be.cryptoCore.AEADBackend != cryptocore.BackendAESSIV {
- panic("MUST NOT use deterministic nonces unless in AESSIV mode!")
- }
- l := be.cryptoCore.IVLen
- nonce = make([]byte, l)
- copy(nonce, fileID)
- // Add the block number to the last 8 byte. Plus one so the block-zero
- // IV is distinct from the fileID.
- counter := binary.BigEndian.Uint64(nonce[l-8 : l])
- binary.BigEndian.PutUint64(nonce[l-8:l], counter+blockNo+1)
- case RandomNonce:
- // Get a fresh random nonce
- nonce = be.cryptoCore.IVGenerator.Get()
- default:
- panic("invalid nonce mode")
- }
if len(nonce) != be.cryptoCore.IVLen {
panic("wrong nonce length")
}
diff --git a/internal/fusefrontend/file.go b/internal/fusefrontend/file.go
index aca7d90..a04b6af 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, contentenc.RandomNonce, nil)
+ blockData = f.contentEnc.EncryptBlock(blockData, b.BlockNo, f.header.Id)
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 295d011..62146a2 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, contentenc.RandomNonce, nil)
+ cBinTarget := fs.contentEnc.EncryptBlock([]byte(target), 0, nil)
cTarget := base64.URLEncoding.EncodeToString(cBinTarget)
// Handle long file name
diff --git a/internal/fusefrontend_reverse/reverse_longnames.go b/internal/fusefrontend_reverse/reverse_longnames.go
index 6409d95..b3d21c9 100644
--- a/internal/fusefrontend_reverse/reverse_longnames.go
+++ b/internal/fusefrontend_reverse/reverse_longnames.go
@@ -84,7 +84,7 @@ func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) {
if err != nil {
return nil, fuse.ToStatus(err)
}
- dirIV := derivePathIV(cDir)
+ dirIV := derivePathIV(cDir, ivPurposeDirIV)
e, err := rfs.findLongnameParent(pDir, dirIV, longname)
if err != nil {
return nil, fuse.ToStatus(err)
diff --git a/internal/fusefrontend_reverse/rfile.go b/internal/fusefrontend_reverse/rfile.go
index 8c5e5f3..2656b7b 100644
--- a/internal/fusefrontend_reverse/rfile.go
+++ b/internal/fusefrontend_reverse/rfile.go
@@ -2,6 +2,7 @@ package fusefrontend_reverse
import (
"bytes"
+ "encoding/binary"
"fmt"
"io"
"os"
@@ -20,6 +21,8 @@ type reverseFile struct {
fd *os.File
// File header (contains the IV)
header contentenc.FileHeader
+ // IV for block 0
+ block0IV []byte
// Content encryption helper
contentEnc *contentenc.ContentEnc
}
@@ -33,7 +36,7 @@ func (rfs *reverseFS) NewFile(relPath string, flags uint32) (nodefs.File, fuse.S
if err != nil {
return nil, fuse.ToStatus(err)
}
- id := derivePathIV(relPath)
+ id := derivePathIV(relPath, ivPurposeFileID)
header := contentenc.FileHeader{
Version: contentenc.CurrentVersion,
Id: id,
@@ -42,6 +45,7 @@ func (rfs *reverseFS) NewFile(relPath string, flags uint32) (nodefs.File, fuse.S
File: nodefs.NewDefaultFile(),
fd: fd,
header: header,
+ block0IV: derivePathIV(relPath, ivPurposeBlock0IV),
contentEnc: rfs.contentEnc,
}, fuse.OK
}
@@ -52,6 +56,26 @@ func (rf *reverseFile) GetAttr(*fuse.Attr) fuse.Status {
return fuse.ENOSYS
}
+// encryptBlocks - encrypt "plaintext" into a number of ciphertext blocks.
+// "plaintext" must already be block-aligned.
+func (rf *reverseFile) encryptBlocks(plaintext []byte, firstBlockNo uint64, fileId []byte, block0IV []byte) []byte {
+ nonce := make([]byte, len(block0IV))
+ copy(nonce, block0IV)
+ block0IVlow := binary.BigEndian.Uint64(block0IV[8:])
+ nonceLow := nonce[8:]
+
+ inBuf := bytes.NewBuffer(plaintext)
+ var outBuf bytes.Buffer
+ bs := int(rf.contentEnc.PlainBS())
+ for blockNo := firstBlockNo; inBuf.Len() > 0; blockNo++ {
+ binary.BigEndian.PutUint64(nonceLow, block0IVlow+blockNo)
+ inBlock := inBuf.Next(bs)
+ outBlock := rf.contentEnc.EncryptBlockNonce(inBlock, blockNo, fileId, nonce)
+ outBuf.Write(outBlock)
+ }
+ return outBuf.Bytes()
+}
+
// readBackingFile: read from the backing plaintext file, encrypt it, return the
// ciphertext.
// "off" ... ciphertext offset (must be >= HEADER_LEN)
@@ -71,7 +95,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, rf.header.Id, contentenc.ReverseDeterministicNonce)
+ ciphertext := rf.encryptBlocks(plaintext, blocks[0].BlockNo, rf.header.Id, rf.block0IV)
// Crop down to the relevant part
lenHave := len(ciphertext)
diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go
index 8411461..1b5e812 100644
--- a/internal/fusefrontend_reverse/rfs.go
+++ b/internal/fusefrontend_reverse/rfs.go
@@ -254,7 +254,7 @@ func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.
nVirtual := 1
// Encrypt names
- dirIV := derivePathIV(cipherPath)
+ dirIV := derivePathIV(cipherPath, ivPurposeDirIV)
for i := range entries {
var cName string
// ".gocryptfs.reverse.conf" in the root directory is mapped to "gocryptfs.conf"
@@ -296,9 +296,9 @@ func (rfs *reverseFS) Readlink(cipherPath string, context *fuse.Context) (string
if rfs.args.PlaintextNames {
return plainTarget, fuse.OK
}
- nonce := derivePathIV(cipherPath)
+ nonce := derivePathIV(cipherPath, ivPurposeSymlinkIV)
// Symlinks are encrypted like file contents and base64-encoded
- cBinTarget := rfs.contentEnc.EncryptBlock([]byte(plainTarget), 0, nil, contentenc.ExternalNonce, nonce)
+ cBinTarget := rfs.contentEnc.EncryptBlockNonce([]byte(plainTarget), 0, nil, nonce)
cTarget := base64.URLEncoding.EncodeToString(cBinTarget)
return cTarget, fuse.OK
}
diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go
index 55fb481..ca8c442 100644
--- a/internal/fusefrontend_reverse/rpath.go
+++ b/internal/fusefrontend_reverse/rpath.go
@@ -19,9 +19,20 @@ func saneDir(path string) string {
return d
}
-// derivePathIV derives an IV from an encrypted path by hashing it
-func derivePathIV(path string) []byte {
- hash := sha256.Sum256([]byte(path))
+type ivPurposeType string
+
+const (
+ ivPurposeDirIV ivPurposeType = "DIRIV"
+ ivPurposeFileID ivPurposeType = "FILEID"
+ ivPurposeSymlinkIV ivPurposeType = "SYMLINKIV"
+ ivPurposeBlock0IV ivPurposeType = "BLOCK0IV"
+)
+
+// derivePathIV derives an IV from an encrypted path by hashing it with sha256
+func derivePathIV(path string, purpose ivPurposeType) []byte {
+ // Use null byte as separator as it cannot occour in the path
+ extended := []byte(path + "\000" + string(purpose))
+ hash := sha256.Sum256(extended)
return hash[:nametransform.DirIVLen]
}
@@ -43,7 +54,7 @@ func (rfs *reverseFS) decryptPath(relPath string) (string, error) {
// Start at the top and recurse
currentDir := filepath.Join(parts[:i]...)
nameType := nametransform.NameType(part)
- dirIV := derivePathIV(currentDir)
+ dirIV := derivePathIV(currentDir, ivPurposeDirIV)
var transformedPart string
if nameType == nametransform.LongNameNone {
transformedPart, err = rfs.nameTransform.DecryptName(part, dirIV)
diff --git a/internal/fusefrontend_reverse/virtualfile.go b/internal/fusefrontend_reverse/virtualfile.go
index 351cbdc..2447e16 100644
--- a/internal/fusefrontend_reverse/virtualfile.go
+++ b/internal/fusefrontend_reverse/virtualfile.go
@@ -14,7 +14,7 @@ func (rfs *reverseFS) newDirIVFile(cRelPath string) (nodefs.File, fuse.Status) {
if err != nil {
return nil, fuse.ToStatus(err)
}
- return rfs.NewVirtualFile(derivePathIV(cDir), absDir)
+ return rfs.NewVirtualFile(derivePathIV(cDir, ivPurposeDirIV), absDir)
}
type virtualFile struct {