diff options
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/configfile/config_file.go | 2 | ||||
| -rw-r--r-- | internal/contentenc/content.go | 58 | ||||
| -rw-r--r-- | internal/fusefrontend/file.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend/fs.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/reverse_longnames.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rfile.go | 28 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rfs.go | 6 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rpath.go | 19 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/virtualfile.go | 2 | 
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 { | 
