contentenc: add safer "bPool" pool variant; add pBlockPool

bPool verifies the lengths of slices going in and out.

Also, add a plaintext block pool - pBlockPool - and use
it for decryption.
This commit is contained in:
Jakob Unterwurzacher 2017-06-29 22:05:23 +02:00
parent 3d32bcd37b
commit 80676c685f
3 changed files with 56 additions and 27 deletions

View File

@ -0,0 +1,39 @@
package contentenc
import (
"log"
"sync"
)
// bPool is a byte slice pool
type bPool struct {
sync.Pool
sliceLen int
}
func newBPool(sliceLen int) bPool {
return bPool{
Pool: sync.Pool{
New: func() interface{} { return make([]byte, sliceLen) },
},
sliceLen: sliceLen,
}
}
// Put grows the slice "s" to its maximum capacity and puts it into the pool.
func (b *bPool) Put(s []byte) {
s = s[:cap(s)]
if len(s) != b.sliceLen {
log.Panicf("wrong len=%d, want=%d", len(s), b.sliceLen)
}
b.Pool.Put(s)
}
// Get returns a byte slice from the pool.
func (b *bPool) Get() (s []byte) {
s = b.Pool.Get().([]byte)
if len(s) != b.sliceLen {
log.Panicf("wrong len=%d, want=%d", len(s), b.sliceLen)
}
return s
}

View File

@ -54,18 +54,19 @@ type ContentEnc struct {
// Force decode even if integrity check fails (openSSL only) // Force decode even if integrity check fails (openSSL only)
forceDecode bool forceDecode bool
// Ciphertext block pool. Always returns cipherBS-sized byte slices. // Ciphertext block pool. Always returns cipherBS-sized byte slices.
cBlockPool sync.Pool cBlockPool bPool
// Ciphertext write pool. Always returns byte slices of size // Ciphertext request data pool. Always returns byte slices of size
// fuse.MAX_KERNEL_WRITE + overhead. // fuse.MAX_KERNEL_WRITE + overhead.
cWritePool sync.Pool CReqPool bPool
cWriteSize int // Plaintext block pool. Always returns plainBS-sized byte slices.
pBlockPool bPool
} }
// New returns an initialized ContentEnc instance. // New returns an initialized ContentEnc instance.
func New(cc *cryptocore.CryptoCore, plainBS uint64, forceDecode bool) *ContentEnc { func New(cc *cryptocore.CryptoCore, plainBS uint64, forceDecode bool) *ContentEnc {
cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen cipherBS := plainBS + uint64(cc.IVLen) + cryptocore.AuthTagLen
// Take IV and GHASH overhead into account. // Take IV and GHASH overhead into account.
cWriteSize := int(fuse.MAX_KERNEL_WRITE / plainBS * cipherBS) cReqSize := int(fuse.MAX_KERNEL_WRITE / plainBS * cipherBS)
if fuse.MAX_KERNEL_WRITE%plainBS != 0 { if fuse.MAX_KERNEL_WRITE%plainBS != 0 {
log.Panicf("unaligned MAX_KERNEL_WRITE=%d", fuse.MAX_KERNEL_WRITE) log.Panicf("unaligned MAX_KERNEL_WRITE=%d", fuse.MAX_KERNEL_WRITE)
} }
@ -76,22 +77,13 @@ func New(cc *cryptocore.CryptoCore, plainBS uint64, forceDecode bool) *ContentEn
allZeroBlock: make([]byte, cipherBS), allZeroBlock: make([]byte, cipherBS),
allZeroNonce: make([]byte, cc.IVLen), allZeroNonce: make([]byte, cc.IVLen),
forceDecode: forceDecode, forceDecode: forceDecode,
cWriteSize: cWriteSize, cBlockPool: newBPool(int(cipherBS)),
CReqPool: newBPool(cReqSize),
pBlockPool: newBPool(int(plainBS)),
} }
c.cBlockPool.New = func() interface{} { return make([]byte, cipherBS) }
c.cWritePool.New = func() interface{} { return make([]byte, cWriteSize) }
return c 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 // PlainBS returns the plaintext block size
func (be *ContentEnc) PlainBS() uint64 { func (be *ContentEnc) PlainBS() uint64 {
return be.plainBS return be.plainBS
@ -119,6 +111,7 @@ func (be *ContentEnc) DecryptBlocks(ciphertext []byte, firstBlockNo uint64, file
} }
} }
pBuf.Write(pBlock) pBuf.Write(pBlock)
be.pBlockPool.Put(pBlock)
firstBlockNo++ firstBlockNo++
} }
return pBuf.Bytes(), err return pBuf.Bytes(), err
@ -158,7 +151,8 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b
ciphertext = ciphertext[be.cryptoCore.IVLen:] ciphertext = ciphertext[be.cryptoCore.IVLen:]
// Decrypt // Decrypt
var plaintext []byte plaintext := be.pBlockPool.Get()
plaintext = plaintext[:0]
aData := make([]byte, 8) aData := make([]byte, 8)
aData = append(aData, fileID...) aData = append(aData, fileID...)
binary.BigEndian.PutUint64(aData, blockNo) binary.BigEndian.PutUint64(aData, blockNo)
@ -210,16 +204,12 @@ func (be *ContentEnc) EncryptBlocks(plaintextBlocks [][]byte, firstBlockNo uint6
be.doEncryptBlocks(plaintextBlocks, ciphertextBlocks, firstBlockNo, fileID) be.doEncryptBlocks(plaintextBlocks, ciphertextBlocks, firstBlockNo, fileID)
} }
// Concatenate ciphertext into a single byte array. // Concatenate ciphertext into a single byte array.
tmp := be.cWritePool.Get().([]byte) tmp := be.CReqPool.Get()
out := bytes.NewBuffer(tmp[:0]) out := bytes.NewBuffer(tmp[:0])
for _, v := range ciphertextBlocks { for _, v := range ciphertextBlocks {
out.Write(v) out.Write(v)
// Return the memory to cBlockPool // Return the memory to cBlockPool
cBlock := v[:cap(v)] be.cBlockPool.Put(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() return out.Bytes()
} }
@ -268,7 +258,7 @@ func (be *ContentEnc) doEncryptBlock(plaintext []byte, blockNo uint64, fileID []
aData = append(aData, fileID...) aData = append(aData, fileID...)
// Get a cipherBS-sized block of memory, copy the nonce into it and truncate to // Get a cipherBS-sized block of memory, copy the nonce into it and truncate to
// nonce length // nonce length
cBlock := be.cBlockPool.Get().([]byte) cBlock := be.cBlockPool.Get()
copy(cBlock, nonce) copy(cBlock, nonce)
cBlock = cBlock[0:len(nonce)] cBlock = cBlock[0:len(nonce)]
// Encrypt plaintext and append to nonce // Encrypt plaintext and append to nonce

View File

@ -311,8 +311,8 @@ func (f *file) doWrite(data []byte, off int64) (uint32, fuse.Status) {
} }
// Write // Write
_, err = f.fd.WriteAt(ciphertext, cOff) _, err = f.fd.WriteAt(ciphertext, cOff)
// Return memory to cWritePool // Return memory to CReqPool
f.fs.contentEnc.CWritePut(ciphertext) f.fs.contentEnc.CReqPool.Put(ciphertext)
if err != nil { if err != nil {
tlog.Warn.Printf("doWrite: Write failed: %s", err.Error()) tlog.Warn.Printf("doWrite: Write failed: %s", err.Error())
return 0, fuse.ToStatus(err) return 0, fuse.ToStatus(err)