From 719e121c74983ac9c5c9f5fc95323c3188bb0f06 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 23 Nov 2014 22:58:28 +0100 Subject: [PATCH] Use large, dynamic buffer for encrypting maps --- archiver_test.go | 2 +- contenthandler.go | 14 ++++++++++++-- key.go | 13 ++++++++----- key_int_test.go | 2 +- key_test.go | 25 +++++++++++++++++++++++++ pools.go | 2 +- 6 files changed, 48 insertions(+), 10 deletions(-) diff --git a/archiver_test.go b/archiver_test.go index 7739847d4..b02310fc3 100644 --- a/archiver_test.go +++ b/archiver_test.go @@ -48,7 +48,7 @@ func BenchmarkChunkEncrypt(b *testing.B) { ok(b, err) - buf := make([]byte, khepri.MaxCiphertextSize) + buf := make([]byte, khepri.CiphertextExtension+chunker.MaxSize) _, err = key.Encrypt(buf, chunk_data.Data) ok(b, err) } diff --git a/contenthandler.go b/contenthandler.go index c4f802862..e1ab13749 100644 --- a/contenthandler.go +++ b/contenthandler.go @@ -81,13 +81,23 @@ func (ch *ContentHandler) Save(t backend.Type, data []byte) (Blob, error) { Size: uint64(len(data)), } + var ciphertext []byte + + // for a bloblist/map, use a larger buffer + if t == backend.Map { + ciphertext = make([]byte, len(data)+CiphertextExtension) + } else { + // otherwise use buffer from pool + ciphertext = GetChunkBuf("ch.Save()") + defer FreeChunkBuf("ch.Save()", ciphertext) + } + // encrypt blob - ciphertext := GetChunkBuf("ch.Save()") - defer FreeChunkBuf("ch.Save()", ciphertext) n, err := ch.key.Encrypt(ciphertext, data) if err != nil { return Blob{}, err } + ciphertext = ciphertext[:n] // save blob diff --git a/key.go b/key.go index 300847001..61acad949 100644 --- a/key.go +++ b/key.go @@ -21,16 +21,19 @@ import ( ) // max size is 8MiB, defined in chunker -const maxDataSize = chunker.MaxSize const ivSize = aes.BlockSize const hmacSize = sha256.Size -const MaxCiphertextSize = ivSize + maxDataSize + hmacSize +const maxCiphertextSize = ivSize + chunker.MaxSize + hmacSize +const CiphertextExtension = ivSize + hmacSize var ( // ErrUnauthenticated is returned when ciphertext verification has failed. ErrUnauthenticated = errors.New("ciphertext verification failed") // ErrNoKeyFound is returned when no key for the repository could be decrypted. ErrNoKeyFound = errors.New("no key could be found") + // ErrBufferTooSmall is returned when the destination slice is too small + // for the ciphertext. + ErrBufferTooSmall = errors.New("destination buffer too small") ) // TODO: figure out scrypt values on the fly depending on the current @@ -254,12 +257,12 @@ func (k *Key) newIV(buf []byte) error { // HMAC. Encrypt returns the ciphertext's length. For the hash function, SHA256 // is used, so the overhead is 16+32=48 byte. func (k *Key) encrypt(ks *keys, ciphertext, plaintext []byte) (int, error) { - if cap(ciphertext) < MaxCiphertextSize { + if cap(ciphertext) < maxCiphertextSize { panic("encryption buffer is too small") } - if len(plaintext) > maxDataSize { - panic("plaintext is too large") + if cap(ciphertext) < len(plaintext)+ivSize+hmacSize { + return 0, ErrBufferTooSmall } _, err := io.ReadFull(rand.Reader, ciphertext[:ivSize]) diff --git a/key_int_test.go b/key_int_test.go index a6249bfdf..ffdd13bcd 100644 --- a/key_int_test.go +++ b/key_int_test.go @@ -48,7 +48,7 @@ func TestCrypto(t *testing.T) { Sign: tv.skey, } - msg := make([]byte, MaxCiphertextSize) + msg := make([]byte, maxCiphertextSize) n, err := r.encrypt(r.master, msg, tv.plaintext) if err != nil { t.Fatal(err) diff --git a/key_test.go b/key_test.go index 6bf4a284d..366002617 100644 --- a/key_test.go +++ b/key_test.go @@ -9,6 +9,7 @@ import ( "github.com/fd0/khepri" "github.com/fd0/khepri/backend" + "github.com/fd0/khepri/chunker" ) var testPassword = "foobar" @@ -72,6 +73,30 @@ func TestEncryptDecrypt(t *testing.T) { } } +func TestLargeEncrypt(t *testing.T) { + be := setupBackend(t) + defer teardownBackend(t, be) + k := setupKey(t, be, testPassword) + + for _, size := range []int{chunker.MaxSize, chunker.MaxSize + 1} { + data := make([]byte, size) + f, err := os.Open("/dev/urandom") + ok(t, err) + + _, err = io.ReadFull(f, data) + ok(t, err) + + ciphertext := make([]byte, size+khepri.CiphertextExtension) + n, err := k.Encrypt(ciphertext, data) + ok(t, err) + + plaintext, err := k.Decrypt(ciphertext[:n]) + ok(t, err) + + equals(t, plaintext, data) + } +} + func BenchmarkEncrypt(b *testing.B) { size := 8 << 20 // 8MiB data := make([]byte, size) diff --git a/pools.go b/pools.go index a346c1e32..c13857eb7 100644 --- a/pools.go +++ b/pools.go @@ -34,7 +34,7 @@ func newChunkBuf() interface{} { chunk_stats.m.Unlock() // create buffer for iv, data and hmac - return make([]byte, MaxCiphertextSize) + return make([]byte, maxCiphertextSize) } func newNode() interface{} {