reverse: derive file ID and block IVs from file paths

This commit is contained in:
Jakob Unterwurzacher 2016-09-25 11:20:10 +02:00
parent 3a9bd92754
commit 7bbf6ad6ea
6 changed files with 41 additions and 37 deletions

View File

@ -19,9 +19,9 @@ const (
// We always use 128-bit IVs for file content encryption // We always use 128-bit IVs for file content encryption
IVBitLen = 128 IVBitLen = 128
_ = iota // skip zero _ = iota // skip zero
RandomNonce NonceMode = iota RandomNonce NonceMode = iota
ReverseDummyNonce NonceMode = iota ReverseDeterministicNonce NonceMode = iota
) )
type ContentEnc struct { type ContentEnc struct {
@ -147,12 +147,17 @@ func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []by
var nonce []byte var nonce []byte
switch nMode { switch nMode {
case ReverseDummyNonce: case ReverseDeterministicNonce:
if be.cryptoCore.AEADBackend != cryptocore.BackendGCMSIV { 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) l := IVBitLen / 8
binary.BigEndian.PutUint64(nonce, blockNo) 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: case RandomNonce:
// Get a fresh random nonce // Get a fresh random nonce
nonce = be.cryptoCore.IVGenerator.Get() nonce = be.cryptoCore.IVGenerator.Get()

View File

@ -9,10 +9,9 @@ import (
"github.com/rfjakob/gocryptfs/internal/nametransform" "github.com/rfjakob/gocryptfs/internal/nametransform"
) )
// deriveDirIV derives the DirIV from the encrypted directory path by // derivePathIV derives an IV from an encrypted path by hashing it
// hashing it func derivePathIV(path string) []byte {
func deriveDirIV(dirPath string) []byte { hash := sha256.Sum256([]byte(path))
hash := sha256.Sum256([]byte(dirPath))
return hash[:nametransform.DirIVLen] return hash[:nametransform.DirIVLen]
} }
@ -22,5 +21,5 @@ func (rfs *reverseFS) newDirIVFile(cRelPath string) (nodefs.File, fuse.Status) {
if err != nil { if err != nil {
return nil, fuse.ToStatus(err) return nil, fuse.ToStatus(err)
} }
return rfs.NewVirtualFile(deriveDirIV(cDir), absDir) return rfs.NewVirtualFile(derivePathIV(cDir), absDir)
} }

View File

@ -89,7 +89,7 @@ func (rfs *reverseFS) newNameFile(relPath string) (nodefs.File, fuse.Status) {
if err != nil { if err != nil {
return nil, fuse.ToStatus(err) return nil, fuse.ToStatus(err)
} }
dirIV := deriveDirIV(cDir) dirIV := derivePathIV(cDir)
e, err := rfs.findLongnameParent(pDir, dirIV, longname) e, err := rfs.findLongnameParent(pDir, dirIV, longname)
if err != nil { if err != nil {
return nil, fuse.ToStatus(err) return nil, fuse.ToStatus(err)

View File

@ -13,29 +13,32 @@ import (
"github.com/rfjakob/gocryptfs/internal/tlog" "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 { type reverseFile struct {
// Embed nodefs.defaultFile for a ENOSYS implementation of all methods // Embed nodefs.defaultFile for a ENOSYS implementation of all methods
nodefs.File nodefs.File
// Backing FD // Backing FD
fd *os.File fd *os.File
// File header (contains the IV)
header contentenc.FileHeader
// Content encryption helper // Content encryption helper
contentEnc *contentenc.ContentEnc 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{ return &reverseFile{
File: nodefs.NewDefaultFile(), File: nodefs.NewDefaultFile(),
fd: fd, fd: fd,
contentEnc: contentEnc, header: contentenc.FileHeader{contentenc.CurrentVersion, id},
contentEnc: rfs.contentEnc,
}, fuse.OK }, fuse.OK
} }
@ -64,7 +67,7 @@ func (rf *reverseFile) readBackingFile(off uint64, length uint64) (out []byte, e
plaintext = plaintext[0:n] plaintext = plaintext[0:n]
// Encrypt blocks // 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 // Crop down to the relevant part
lenHave := len(ciphertext) lenHave := len(ciphertext)
@ -88,7 +91,7 @@ func (rf *reverseFile) Read(buf []byte, ioff int64) (resultData fuse.ReadResult,
// Synthesize file header // Synthesize file header
if off < contentenc.HEADER_LEN { if off < contentenc.HEADER_LEN {
header = zeroFileHeader.Pack() header = rf.header.Pack()
// Truncate to requested part // Truncate to requested part
end := int(off) + len(buf) end := int(off) + len(buf)
if end > len(header) { 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 return fuse.ReadResultData(out.Bytes()), fuse.OK
} }
// Release - FUSE call, close file
func (rf *reverseFile) Release() {
rf.fd.Close()
}

View File

@ -226,15 +226,7 @@ func (rfs *reverseFS) Open(relPath string, flags uint32, context *fuse.Context)
if rfs.isFiltered(relPath) { if rfs.isFiltered(relPath) {
return nil, fuse.EPERM return nil, fuse.EPERM
} }
absPath, err := rfs.abs(rfs.decryptPath(relPath)) return rfs.NewFile(relPath, flags)
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)
} }
// OpenDir - FUSE readdir call // OpenDir - FUSE readdir call
@ -258,7 +250,7 @@ func (rfs *reverseFS) OpenDir(cipherPath string, context *fuse.Context) ([]fuse.
nVirtual := 1 nVirtual := 1
// Encrypt names // Encrypt names
dirIV := deriveDirIV(cipherPath) dirIV := derivePathIV(cipherPath)
for i := range entries { for i := range entries {
var cName string var cName string
// ".gocryptfs.reverse.conf" in the root directory is mapped to "gocryptfs.conf" // ".gocryptfs.reverse.conf" in the root directory is mapped to "gocryptfs.conf"

View File

@ -36,7 +36,7 @@ func (rfs *reverseFS) decryptPath(relPath string) (string, error) {
// Start at the top and recurse // Start at the top and recurse
currentDir := filepath.Join(parts[:i]...) currentDir := filepath.Join(parts[:i]...)
nameType := nametransform.NameType(part) nameType := nametransform.NameType(part)
dirIV := deriveDirIV(currentDir) dirIV := derivePathIV(currentDir)
var transformedPart string var transformedPart string
if nameType == nametransform.LongNameNone { if nameType == nametransform.LongNameNone {
transformedPart, err = rfs.nameTransform.DecryptName(part, dirIV) transformedPart, err = rfs.nameTransform.DecryptName(part, dirIV)