reverse: derive file ID and block IVs from file paths
This commit is contained in:
parent
3a9bd92754
commit
7bbf6ad6ea
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue