diff --git a/cryptfs/cryptfs_content.go b/cryptfs/cryptfs_content.go index 0ebf34d..512bca9 100644 --- a/cryptfs/cryptfs_content.go +++ b/cryptfs/cryptfs_content.go @@ -3,6 +3,7 @@ package cryptfs // File content encryption / decryption import ( + "bytes" "os" "errors" "crypto/cipher" @@ -13,7 +14,23 @@ type CryptFile struct { gcm cipher.AEAD } -// decryptBlock - Verify and decrypt GCM block +// DecryptBlocks - Decrypt a number of blocks +func (be *CryptFS) DecryptBlocks(ciphertext []byte) ([]byte, error) { + cBuf := bytes.NewBuffer(ciphertext) + var err error + var pBuf bytes.Buffer + for cBuf.Len() > 0 { + cBlock := cBuf.Next(int(be.cipherBS)) + pBlock, err := be.DecryptBlock(cBlock) + if err != nil { + break + } + pBuf.Write(pBlock) + } + return pBuf.Bytes(), err +} + +// DecryptBlock - Verify and decrypt GCM block func (be *CryptFS) DecryptBlock(ciphertext []byte) ([]byte, error) { // Empty block? @@ -92,3 +109,29 @@ func (be *CryptFS) minu64(x uint64, y uint64) uint64 { } return y } + +// Get the byte range in the ciphertext corresponding to blocks +// (full blocks!) +func (be *CryptFS) JoinCiphertextRange(blocks []intraBlock) (uint64, uint64) { + + offset, _ := blocks[0].CiphertextRange() + last := blocks[len(blocks)-1] + length := (last.BlockNo - blocks[0].BlockNo + 1) * be.cipherBS + + return offset, length +} + +// Crop plaintext that correspons to complete cipher blocks down to what is +// requested according to "iblocks" +func (be *CryptFS) CropPlaintext(plaintext []byte, blocks []intraBlock) []byte { + offset := blocks[0].Offset + last := blocks[len(blocks)-1] + length := (last.BlockNo - blocks[0].BlockNo + 1) * be.plainBS + var cropped []byte + if offset + length > uint64(len(plaintext)) { + cropped = plaintext[offset:len(plaintext)] + } else { + cropped = plaintext[offset:offset+length] + } + return cropped +} diff --git a/frontend/fe_file.go b/frontend/fe_file.go index 7cc23ad..b417227 100644 --- a/frontend/fe_file.go +++ b/frontend/fe_file.go @@ -67,26 +67,32 @@ func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenR } func (f *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { + + fmt.Printf("Read: o=%d l=%d\n", req.Offset, req.Size) + + // Read the backing ciphertext in one go iblocks := f.crfs.SplitRange(uint64(req.Offset), uint64(req.Size)) - for _, ib := range iblocks { - var partReq fuse.ReadRequest - var partResp fuse.ReadResponse - o, l := ib.CiphertextRange() - partReq.Offset = int64(o) - partReq.Size = int(l) - partResp.Data = make([]byte, int(l)) - err := f.File.Read(ctx, &partReq, &partResp) - if err != nil { - return err - } - plaintext, err := f.crfs.DecryptBlock(partResp.Data) - if err != nil { - fmt.Printf("Read: Error reading block %d: %s\n", ib.BlockNo, err.Error()) - return err - } - plaintext = ib.CropBlock(plaintext) - resp.Data = append(resp.Data, plaintext...) + var cipherReq fuse.ReadRequest + var cipherResp fuse.ReadResponse + o, l := f.crfs.JoinCiphertextRange(iblocks) + cipherResp.Data = make([]byte, int(l)) + cipherReq.Offset = int64(o) + cipherReq.Size = int(l) + cryptfs.Debug.Printf("Read: cipherReq o=%d l=%d\n", o, l) + err := f.File.Read(ctx, &cipherReq, &cipherResp) + if err != nil { + return err } + + // Decrypt it + plaintext, err := f.crfs.DecryptBlocks(cipherResp.Data) + if err != nil { + resp.Data = plaintext + return err + } + // Crop down to relevant part + resp.Data = f.crfs.CropPlaintext(plaintext, iblocks) + return nil } @@ -98,6 +104,7 @@ func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.Wri for _, ib := range iblocks { if ib.IsPartial() { // RMW + cryptfs.Debug.Printf("RMW\n") blockData = make([]byte, f.crfs.PlainBS()) var readReq fuse.ReadRequest var readResp fuse.ReadResponse @@ -127,8 +134,9 @@ func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.Wri return err } // Remove written data from the front of the request - req.Data = req.Data[len(blockData):len(req.Data)] - resp.Size += len(blockData) + cryptfs.Debug.Printf("req.Data[%d:%d]\n", int(ib.Length), len(req.Data)) + req.Data = req.Data[int(ib.Length):len(req.Data)] + resp.Size += int(ib.Length) } return nil }