aboutsummaryrefslogtreecommitdiff
path: root/internal/contentenc
diff options
context:
space:
mode:
Diffstat (limited to 'internal/contentenc')
-rw-r--r--internal/contentenc/content.go56
1 files changed, 46 insertions, 10 deletions
diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go
index 3f578e8..9bc1c50 100644
--- a/internal/contentenc/content.go
+++ b/internal/contentenc/content.go
@@ -10,6 +10,8 @@ import (
"runtime"
"sync"
+ "github.com/hanwen/go-fuse/fuse"
+
"github.com/rfjakob/gocryptfs/internal/cryptocore"
"github.com/rfjakob/gocryptfs/internal/stupidgcm"
"github.com/rfjakob/gocryptfs/internal/tlog"
@@ -51,20 +53,43 @@ type ContentEnc struct {
allZeroNonce []byte
// Force decode even if integrity check fails (openSSL only)
forceDecode bool
+ // Ciphertext block pool. Always returns cipherBS-sized byte slices.
+ cBlockPool sync.Pool
+ // Ciphertext write pool. Always returns byte slices of size
+ // fuse.MAX_KERNEL_WRITE + overhead.
+ cWritePool sync.Pool
+ cWriteSize int
}
// New returns an initialized ContentEnc instance.
func New(cc *cryptocore.CryptoCore, plainBS uint64, forceDecode bool) *ContentEnc {
cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen
-
- return &ContentEnc{
+ // Take IV and GHASH overhead into account.
+ cWriteSize := int(fuse.MAX_KERNEL_WRITE / plainBS * cipherBS)
+ if fuse.MAX_KERNEL_WRITE%plainBS != 0 {
+ log.Panicf("unaligned MAX_KERNEL_WRITE=%d", fuse.MAX_KERNEL_WRITE)
+ }
+ c := &ContentEnc{
cryptoCore: cc,
plainBS: plainBS,
cipherBS: cipherBS,
allZeroBlock: make([]byte, cipherBS),
allZeroNonce: make([]byte, cc.IVLen),
forceDecode: forceDecode,
+ cWriteSize: cWriteSize,
+ }
+ c.cBlockPool.New = func() interface{} { return make([]byte, cipherBS) }
+ c.cWritePool.New = func() interface{} { return make([]byte, cWriteSize) }
+ return c
+}
+
+// CWritePut puts "buf" back into the cWritePool.
+func (be *ContentEnc) CWritePut(buf []byte) {
+ buf = buf[:cap(buf)]
+ if len(buf) != be.cWriteSize {
+ log.Panicf("wrong len=%d, want=%d", len(buf), be.cWriteSize)
}
+ be.cWritePool.Put(buf)
}
// PlainBS returns the plaintext block size
@@ -185,12 +210,16 @@ func (be *ContentEnc) EncryptBlocks(plaintextBlocks [][]byte, firstBlockNo uint6
be.doEncryptBlocks(plaintextBlocks, ciphertextBlocks, firstBlockNo, fileID)
}
// Concatenate ciphertext into a single byte array.
- // Size the output buffer for the maximum possible size (all blocks complete)
- // to prevent further allocations in out.Write()
- tmp := make([]byte, len(plaintextBlocks)*int(be.CipherBS()))
+ tmp := be.cWritePool.Get().([]byte)
out := bytes.NewBuffer(tmp[:0])
for _, v := range ciphertextBlocks {
out.Write(v)
+ // Return the memory to cBlockPool
+ cBlock := v[:cap(v)]
+ if len(cBlock) != int(be.cipherBS) {
+ log.Panicf("unexpected cBlock length: len=%d cipherBS=%d", len(cBlock), be.cipherBS)
+ }
+ be.cBlockPool.Put(cBlock)
}
return out.Bytes()
}
@@ -233,15 +262,22 @@ func (be *ContentEnc) doEncryptBlock(plaintext []byte, blockNo uint64, fileID []
if len(nonce) != be.cryptoCore.IVLen {
log.Panic("wrong nonce length")
}
-
- // Authenticate block with block number and file ID
+ // Block is authenticated with block number and file ID
aData := make([]byte, 8)
binary.BigEndian.PutUint64(aData, blockNo)
aData = append(aData, fileID...)
-
+ // Get a cipherBS-sized block of memory, copy the nonce into it and truncate to
+ // nonce length
+ cBlock := be.cBlockPool.Get().([]byte)
+ copy(cBlock, nonce)
+ cBlock = cBlock[0:len(nonce)]
// Encrypt plaintext and append to nonce
- ciphertext := be.cryptoCore.AEADCipher.Seal(nonce, nonce, plaintext, aData)
-
+ ciphertext := be.cryptoCore.AEADCipher.Seal(cBlock, nonce, plaintext, aData)
+ overhead := int(be.cipherBS - be.plainBS)
+ if len(plaintext)+overhead != len(ciphertext) {
+ log.Panicf("unexpected ciphertext length: plaintext=%d, overhead=%d, ciphertext=%d",
+ len(plaintext), overhead, len(ciphertext))
+ }
return ciphertext
}