diff options
| -rw-r--r-- | cryptfs/cryptfile.go | 100 | ||||
| -rw-r--r-- | frontend/dir.go | 15 | ||||
| -rw-r--r-- | frontend/file.go | 51 | 
3 files changed, 144 insertions, 22 deletions
| diff --git a/cryptfs/cryptfile.go b/cryptfs/cryptfile.go index 5645f3c..0cd11da 100644 --- a/cryptfs/cryptfile.go +++ b/cryptfs/cryptfile.go @@ -15,6 +15,50 @@ type CryptFile struct {  	cipherBS int64  } +// decryptBlock - Verify and decrypt GCM block +func (be *CryptFS) DecryptBlock(ciphertext []byte) ([]byte, error) { + +	// Empty block? +	if len(ciphertext) == 0 { +		return ciphertext, nil +	} + +	if len(ciphertext) < NONCE_LEN { +		warn.Printf("decryptBlock: Block is too short: %d bytes\n", len(ciphertext)) +		return nil, errors.New("Block is too short") +	} + +	// Extract nonce +	nonce := ciphertext[:NONCE_LEN] +	ciphertext = ciphertext[NONCE_LEN:] + +	// Decrypt +	var plaintext []byte +	plaintext, err := be.gcm.Open(plaintext, nonce, ciphertext, nil) +	if err != nil { +		return nil, err +	} + +	return plaintext, nil +} + +// encryptBlock - Encrypt and add MAC using GCM +func (be *CryptFS) EncryptBlock(plaintext []byte) []byte { + +	// Empty block? +	if len(plaintext) == 0 { +		return plaintext +	} + +	// Get fresh nonce +	nonce := gcmNonce.Get() + +	// Encrypt plaintext and append to nonce +	ciphertext := be.gcm.Seal(nonce, nonce, plaintext, nil) + +	return ciphertext +} +  // readCipherBlock - Read ciphertext block number "blockNo", decrypt,  // return plaintext  func (be *CryptFile) readCipherBlock(blockNo int64) ([]byte, error) { @@ -30,8 +74,7 @@ func (be *CryptFile) readCipherBlock(blockNo int64) ([]byte, error) {  	// Truncate buffer to actually read bytes  	buf = buf[:readN] -	// Empty block?file:///home/jakob/go/src/github.com/rfjakob/gocryptfs-bazil/backend/backend.go - +	// Empty block?  	if len(buf) == 0 {  		return buf, nil  	} @@ -58,28 +101,49 @@ func (be *CryptFile) readCipherBlock(blockNo int64) ([]byte, error) {  // intraBlock identifies a part of a file block  type intraBlock struct { -	blockNo int64  // Block number in file -	offset  int64  // Offset into block plaintext -	length  int64  // Length of data from this block +	BlockNo int64  // Block number in file +	Offset  int64  // Offset into block plaintext +	Length  int64  // Length of data from this block +	fs    *CryptFS +} + +// isPartial - is the block partial? This means we have to do read-modify-write. +func (ib *intraBlock) IsPartial() bool { +	if ib.Offset > 0 || ib.Length < ib.fs.plainBS { +		return true +	} +	return false +} + +// ciphertextRange - get byte range in ciphertext file corresponding to BlockNo +func (ib *intraBlock) CiphertextRange() (offset int64, length int64) { +	return ib.BlockNo * ib.fs.cipherBS, ib.fs.cipherBS +} + +// CropBlock - crop a full plaintext block down to the relevant part +func (ib *intraBlock) CropBlock(d []byte) []byte{ +	return d[ib.Offset:ib.Offset+ib.Length]  }  // Split a plaintext byte range into (possible partial) blocks -func (be *CryptFile) splitRange(offset int64, length int64) []intraBlock { +func (be *CryptFS) SplitRange(offset int64, length int64) []intraBlock {  	var b intraBlock  	var parts []intraBlock +	b.fs = be +  	for length > 0 { -		b.blockNo = offset / be.plainBS -		b.offset = offset % be.plainBS -		b.length = be.min64(length, be.plainBS - b.offset) +		b.BlockNo = offset / be.plainBS +		b.Offset = offset % be.plainBS +		b.Length = be.min64(length, be.plainBS - b.Offset)  		parts = append(parts, b) -		offset += b.length -		length -= b.length +		offset += b.Length +		length -= b.Length  	}  	return parts  } -func (be *CryptFile) min64(x int64, y int64) int64 { +func (be *CryptFS) min64(x int64, y int64) int64 {  	if x < y {  		return x  	} @@ -110,16 +174,16 @@ func (be *CryptFile) writeCipherBlock(blockNo int64, plain []byte) error {  // Perform RMW cycle on block  // Write "data" into file location specified in "b"  func (be *CryptFile) rmwWrite(b intraBlock, data []byte, f *os.File) error { -	if b.length != int64(len(data)) { +	if b.Length != int64(len(data)) {  		panic("Length mismatch")  	} -	oldBlock, err := be.readCipherBlock(b.blockNo) +	oldBlock, err := be.readCipherBlock(b.BlockNo)  	if err != nil {  		return err  	} -	newBlockLen := b.offset + b.length -	debug.Printf("newBlockLen := %d + %d\n", b.offset, b.length) +	newBlockLen := b.Offset + b.Length +	debug.Printf("newBlockLen := %d + %d\n", b.Offset, b.Length)  	var newBlock []byte  	// Write goes beyond the old block and grows the file? @@ -133,10 +197,10 @@ func (be *CryptFile) rmwWrite(b intraBlock, data []byte, f *os.File) error {  	// Fill with old data  	copy(newBlock, oldBlock)  	// Then overwrite the relevant parts with new data -	copy(newBlock[b.offset:b.offset + b.length], data) +	copy(newBlock[b.Offset:b.Offset + b.Length], data)  	// Actual write -	err = be.writeCipherBlock(b.blockNo, newBlock) +	err = be.writeCipherBlock(b.BlockNo, newBlock)  	if err != nil {  		// An incomplete write to a ciphertext block means that the whole block diff --git a/frontend/dir.go b/frontend/dir.go index eca3cf5..ce64a5a 100644 --- a/frontend/dir.go +++ b/frontend/dir.go @@ -45,10 +45,16 @@ func (d *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.Lo  	}  	clueDir, ok := n.(*cluefs.Dir)  	if ok { -		return &Dir { Dir: clueDir }, nil +		return &Dir { +			Dir: clueDir, +			crfs: d.crfs, +		}, nil  	} else {  		clueFile := n.(*cluefs.File) -		return &File { File: clueFile }, nil +		return &File { +			File: clueFile, +			crfs: d.crfs, +		}, nil  	}  } @@ -103,6 +109,9 @@ func (d *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.Cr  		return nil, nil, err  	}  	clueFile := n.(*cluefs.File) -	cryptFile := &File {File: clueFile} +	cryptFile := &File { +		File: clueFile, +		crfs: d.crfs, +	}  	return cryptFile, cryptFile, nil  } diff --git a/frontend/file.go b/frontend/file.go index 81590e8..c5a02be 100644 --- a/frontend/file.go +++ b/frontend/file.go @@ -1,11 +1,60 @@  package frontend  import ( +	"fmt"  	"github.com/rfjakob/gocryptfs/cryptfs"  	"github.com/rfjakob/cluefs/lib/cluefs" + +	"bazil.org/fuse" +	fusefs "bazil.org/fuse/fs" +	"golang.org/x/net/context"  )  type File struct { -	*cryptfs.CryptFile  	*cluefs.File +	crfs *cryptfs.CryptFS +} + +func (f *File) Open(ctx context.Context, req *fuse.OpenRequest, resp *fuse.OpenResponse) (fusefs.Handle, error) { +	fmt.Printf("File.Open: f.crfs=%p\n", f.crfs) +	h, err := f.File.Open(ctx, req, resp) +	if err != nil { +		return nil, err +	} +	clueFile := h.(*cluefs.File) +	return &File { +		File: clueFile, +		crfs: f.crfs, +	}, nil +} + +func (f *File) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) error { +	fmt.Printf("File.Read: f.crfs=%p\n", f.crfs) +	iblocks := f.crfs.SplitRange(req.Offset, int64(req.Size)) +	for _, ib := range iblocks { +		var partReq fuse.ReadRequest +		var partResp fuse.ReadResponse +		o, l := ib.CiphertextRange() +		partReq.Size = int(l) +		partResp.Data = make([]byte, int(l)) +		partReq.Offset = o +		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: Block %d: %s\n", ib.BlockNo, err.Error()) +			return err +		} +		plaintext = ib.CropBlock(partResp.Data) +		resp.Data = append(resp.Data, plaintext...) +	} +	return nil +} + +func (f *File) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) error { +	iblocks := f.crfs.SplitRange(req.Offset, int64(len(req.Data))) +	iblocks = iblocks +	return nil  } | 
