From a2510efe12d2720399afcd8baea0c6634d4779e6 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Thu, 29 Sep 2016 21:29:45 +0200 Subject: reverse: use per-purpose nonce generation Also pull all the deterministic nonce code into fusefrontend_reverse to greatly simplify the normal code path. --- internal/fusefrontend_reverse/reverse_longnames.go | 2 +- internal/fusefrontend_reverse/rfile.go | 28 ++++++++++++++++++++-- internal/fusefrontend_reverse/rfs.go | 6 ++--- internal/fusefrontend_reverse/rpath.go | 19 +++++++++++---- internal/fusefrontend_reverse/virtualfile.go | 2 +- 5 files changed, 46 insertions(+), 11 deletions(-) (limited to 'internal/fusefrontend_reverse') 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 { -- cgit v1.2.3