From d0bc7970f721cee607d993406d97d32e2c660abe Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sun, 5 Mar 2017 21:59:55 +0100 Subject: full stack: implement HKDF support ...but keep it disabled by default for new filesystems. We are still missing an example filesystem and CLI arguments to explicitely enable and disable it. --- internal/cryptocore/cryptocore.go | 74 +++++++++++++++++++++++----------- internal/cryptocore/cryptocore_test.go | 27 +++++++------ internal/cryptocore/hkdf.go | 21 ++++++++++ 3 files changed, 85 insertions(+), 37 deletions(-) create mode 100644 internal/cryptocore/hkdf.go (limited to 'internal/cryptocore') diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index 7e1d238..5244104 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -51,46 +51,72 @@ type CryptoCore struct { // Even though the "GCMIV128" feature flag is now mandatory, we must still // support 96-bit IVs here because they were used for encrypting the master // key in gocryptfs.conf up to gocryptfs v1.2. v1.3 switched to 128 bits. -func New(key []byte, aeadType AEADTypeEnum, IVBitLen int) *CryptoCore { +func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoCore { if len(key) != KeyLen { log.Panic(fmt.Sprintf("Unsupported key length %d", len(key))) } // We want the IV size in bytes IVLen := IVBitLen / 8 - // Name encryption always uses built-in Go AES through blockCipher. - // Content encryption uses BlockCipher only if useOpenssl=false. - blockCipher, err := aes.NewCipher(key) - if err != nil { - log.Panic(err) + // Initialize EME for filename encryption. + var emeCipher *eme.EMECipher + { + emeKey := key + if useHKDF { + info := "EME filename encryption" + emeKey = hkdfDerive(key, info, KeyLen) + } + emeBlockCipher, err := aes.NewCipher(emeKey) + if err != nil { + log.Panic(err) + } + emeCipher = eme.New(emeBlockCipher) } - emeCipher := eme.New(blockCipher) + // Initilize an AEAD cipher for file content encryption. var aeadCipher cipher.AEAD - switch aeadType { - case BackendOpenSSL: - if IVLen != 16 { - log.Panic("stupidgcm only supports 128-bit IVs") + if aeadType == BackendOpenSSL || aeadType == BackendGoGCM { + gcmKey := key + if useHKDF { + info := "AES-GCM file content encryption" + gcmKey = hkdfDerive(key, info, KeyLen) } - aeadCipher = stupidgcm.New(key) - case BackendGoGCM: - aeadCipher, err = cipher.NewGCMWithNonceSize(blockCipher, IVLen) - case BackendAESSIV: + switch aeadType { + case BackendOpenSSL: + if IVLen != 16 { + log.Panic("stupidgcm only supports 128-bit IVs") + } + aeadCipher = stupidgcm.New(gcmKey) + case BackendGoGCM: + goGcmBlockCipher, err := aes.NewCipher(gcmKey) + if err != nil { + log.Panic(err) + } + aeadCipher, err = cipher.NewGCMWithNonceSize(goGcmBlockCipher, IVLen) + if err != nil { + log.Panic(err) + } + } + } else if aeadType == BackendAESSIV { if IVLen != 16 { // SIV supports any nonce size, but we only use 16. log.Panic("AES-SIV must use 16-byte nonces") } - // AES-SIV uses 1/2 of the key for authentication, 1/2 for - // encryption, so we need a 64-bytes key for AES-256. Derive it from - // the master key by hashing it with SHA-512. - key64 := sha512.Sum512(key) - aeadCipher = siv_aead.New(key64[:]) - default: + var key64 []byte + if useHKDF { + info := "AES-SIV file content encryption" + key64 = hkdfDerive(key, info, siv_aead.KeyLen) + } else { + // AES-SIV uses 1/2 of the key for authentication, 1/2 for + // encryption, so we need a 64-bytes key for AES-256. Derive it from + // the master key by hashing it with SHA-512. + s := sha512.Sum512(key) + key64 = s[:] + } + aeadCipher = siv_aead.New(key64) + } else { log.Panic("unknown backend cipher") } - if err != nil { - log.Panic(err) - } return &CryptoCore{ EMECipher: emeCipher, diff --git a/internal/cryptocore/cryptocore_test.go b/internal/cryptocore/cryptocore_test.go index 252c311..25f6572 100644 --- a/internal/cryptocore/cryptocore_test.go +++ b/internal/cryptocore/cryptocore_test.go @@ -7,18 +7,19 @@ import ( // "New" should accept at least these param combinations func TestCryptoCoreNew(t *testing.T) { key := make([]byte, 32) - - c := New(key, BackendOpenSSL, 128) - if c.IVLen != 16 { - t.Fail() - } - c = New(key, BackendGoGCM, 96) - if c.IVLen != 12 { - t.Fail() - } - c = New(key, BackendGoGCM, 128) - if c.IVLen != 16 { - t.Fail() + for _, useHKDF := range []bool{true, false} { + c := New(key, BackendOpenSSL, 128, useHKDF) + if c.IVLen != 16 { + t.Fail() + } + c = New(key, BackendGoGCM, 96, useHKDF) + if c.IVLen != 12 { + t.Fail() + } + c = New(key, BackendGoGCM, 128, useHKDF) + if c.IVLen != 16 { + t.Fail() + } } } @@ -31,5 +32,5 @@ func TestNewPanic(t *testing.T) { }() key := make([]byte, 16) - New(key, BackendOpenSSL, 128) + New(key, BackendOpenSSL, 128, true) } diff --git a/internal/cryptocore/hkdf.go b/internal/cryptocore/hkdf.go new file mode 100644 index 0000000..6944825 --- /dev/null +++ b/internal/cryptocore/hkdf.go @@ -0,0 +1,21 @@ +package cryptocore + +import ( + "crypto/sha256" + "log" + + "golang.org/x/crypto/hkdf" +) + +// hkdfDerive derives "outLen" bytes from "masterkey" and "info" using +// HKDF-SHA256. +// It returns the derived bytes or panics. +func hkdfDerive(masterkey []byte, info string, outLen int) (out []byte) { + h := hkdf.New(sha256.New, masterkey, nil, []byte(info)) + out = make([]byte, outLen) + n, err := h.Read(out) + if n != outLen || err != nil { + log.Panicf("hkdfDerive: hkdf read failed, got %d bytes, error: %v", n, err) + } + return out +} -- cgit v1.2.3