summaryrefslogtreecommitdiff
path: root/internal/cryptocore
diff options
context:
space:
mode:
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
+}