diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index c638221..98037d0 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -19,9 +19,9 @@ const ( // We always use 128-bit IVs for file content encryption IVBitLen = 128 - _ = iota // skip zero - RandomNonce NonceMode = iota - ReverseDummyNonce NonceMode = iota + _ = iota // skip zero + RandomNonce NonceMode = iota + ReverseDeterministicNonce NonceMode = iota ) type ContentEnc struct { @@ -147,12 +147,17 @@ func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []by var nonce []byte switch nMode { - case ReverseDummyNonce: + case ReverseDeterministicNonce: if be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV { - panic("MUST NOT use dummy nonces unless in GCMSIV mode!") + panic("MUST NOT use deterministic nonces unless in GCMSIV mode!") } - nonce = make([]byte, IVBitLen/8) - binary.BigEndian.PutUint64(nonce, blockNo) + l := IVBitLen / 8 + 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() diff --git a/internal/fusefrontend_reverse/reverse_diriv.go b/internal/fusefrontend_reverse/reverse_diriv.go index df3a4d1..88d0680 100644 --- a/internal/fusefrontend_reverse/reverse_diriv.go +++ b/internal/fusefrontend_reverse/reverse_diriv.go @@ -9,10 +9,9 @@ import ( "github.com/rfjakob/gocryptfs/internal/nametransform" ) -// deriveDirIV derives the DirIV from the encrypted directory path by -// hashing it -func deriveDirIV(dirPath string) []byte { - hash := sha256.Sum256([]byte(dirPath)) +// derivePathIV derives an IV from an encrypted path by hashing it +func derivePathIV(path string) []byte { + hash := sha256.Sum256([]byte(path)) return hash[:nametransform.DirIVLen] } @@ -22,5 +21,5 @@ func (rfs *reverseFS) newDirIVFile(cRelPath string) (nodefs.File, fuse.Status) { if err != nil { return nil, fuse.ToStatus(err) } - return rfs.NewVirtualFile(deriveDirIV(cDir), absDir) + return rfs.NewVirtualFile(derivePathIV(cDir), absDir) } diff --git a/internal/fusefrontend_reverse/reverse_longnames.go b/internal/fusefrontend_reverse/reverse_longnames.go index 487802f..9c45fe8 100644 --- a/internal/fusefrontend_reverse/reverse_longnames.go +++ b/internal/fusefrontend_reverse/reverse_longnames.go @@ -89,7 +89,7 @@ func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) { if err != nil { return nil, fuse.ToStatus(err) } - dirIV := deriveDirIV(cDir) + dirIV := derivePathIV(cDir) 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 7e54b17..4e9b5ba 100644 --- a/internal/fusefrontend_reverse/rfile.go +++ b/internal/fusefrontend_reverse/rfile.go @@ -13,29 +13,32 @@ import ( "github.com/rfjakob/gocryptfs/internal/tlog" ) -// File header that contains an all-zero File ID -var zeroFileHeader *contentenc.FileHeader - -func init() { - zeroFileHeader = contentenc.RandomHeader() - // Overwrite with zeros - zeroFileHeader.Id = make([]byte, contentenc.HEADER_ID_LEN) -} - type reverseFile struct { // Embed nodefs.defaultFile for a ENOSYS implementation of all methods nodefs.File // Backing FD fd *os.File + // File header (contains the IV) + header contentenc.FileHeader // Content encryption helper contentEnc *contentenc.ContentEnc } -func NewFile(fd *os.File, contentEnc *contentenc.ContentEnc) (nodefs.File, fuse.Status) { +func (rfs *reverseFS) NewFile(relPath string, flags uint32) (nodefs.File, fuse.Status) { + absPath, err := rfs.abs(rfs.decryptPath(relPath)) + if err != nil { + return nil, fuse.ToStatus(err) + } + fd, err := os.OpenFile(absPath, int(flags), 0666) + if err != nil { + return nil, fuse.ToStatus(err) + } + id := derivePathIV(relPath) return &reverseFile{ File: nodefs.NewDefaultFile(), fd: fd, - contentEnc: contentEnc, + header: contentenc.FileHeader{contentenc.CurrentVersion, id}, + contentEnc: rfs.contentEnc, }, fuse.OK } @@ -64,7 +67,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, contentenc.ReverseDummyNonce) + ciphertext := rf.contentEnc.EncryptBlocks(plaintext, blocks[0].BlockNo, rf.header.Id, contentenc.ReverseDeterministicNonce) // Crop down to the relevant part lenHave := len(ciphertext) @@ -88,7 +91,7 @@ func (rf *reverseFile) Read(buf []byte, ioff int64) (resultData fuse.ReadResult, // Synthesize file header if off < contentenc.HEADER_LEN { - header = zeroFileHeader.Pack() + header = rf.header.Pack() // Truncate to requested part end := int(off) + len(buf) if end > len(header) { @@ -119,3 +122,8 @@ func (rf *reverseFile) Read(buf []byte, ioff int64) (resultData fuse.ReadResult, return fuse.ReadResultData(out.Bytes()), fuse.OK } + +// Release - FUSE call, close file +func (rf *reverseFile) Release() { + rf.fd.Close() +} diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index 9ebf91c..6089d41 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -226,15 +226,7 @@ func (rfs *reverseFS) Open(relPath string, flags uint32, context *fuse.Context) if rfs.isFiltered(relPath) { return nil, fuse.EPERM } - absPath, err := rfs.abs(rfs.decryptPath(relPath)) - if err != nil { - return nil, fuse.ToStatus(err) - } - f, err := os.OpenFile(absPath, int(flags), 0666) - if err != nil { - return nil, fuse.ToStatus(err) - } - return NewFile(f, rfs.contentEnc) + return rfs.NewFile(relPath, flags) } // OpenDir - FUSE readdir call @@ -258,7 +250,7 @@ func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse. nVirtual := 1 // Encrypt names - dirIV := deriveDirIV(cipherPath) + dirIV := derivePathIV(cipherPath) for i := range entries { var cName string // ".gocryptfs.reverse.conf" in the root directory is mapped to "gocryptfs.conf" diff --git a/internal/fusefrontend_reverse/rpath.go b/internal/fusefrontend_reverse/rpath.go index 19539bb..c603cad 100644 --- a/internal/fusefrontend_reverse/rpath.go +++ b/internal/fusefrontend_reverse/rpath.go @@ -36,7 +36,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 := deriveDirIV(currentDir) + dirIV := derivePathIV(currentDir) var transformedPart string if nameType == nametransform.LongNameNone { transformedPart, err = rfs.nameTransform.DecryptName(part, dirIV)