summaryrefslogtreecommitdiff
path: root/internal/cryptocore
diff options
context:
space:
mode:
authorJakob Unterwurzacher2017-03-05 21:59:55 +0100
committerJakob Unterwurzacher2017-03-05 21:59:55 +0100
commitd0bc7970f721cee607d993406d97d32e2c660abe (patch)
tree894b016af6e7785bb707e3d2e0f660608ceeea06 /internal/cryptocore
parent4fadcbaf68ce25dcdc7665059f43226f5f9a4da5 (diff)
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.
Diffstat (limited to 'internal/cryptocore')
-rw-r--r--internal/cryptocore/cryptocore.go74
-rw-r--r--internal/cryptocore/cryptocore_test.go27
-rw-r--r--internal/cryptocore/hkdf.go21
3 files changed, 85 insertions, 37 deletions
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
+}