diff options
Diffstat (limited to 'internal/cryptocore')
| -rw-r--r-- | internal/cryptocore/cryptocore.go | 74 | ||||
| -rw-r--r-- | internal/cryptocore/cryptocore_test.go | 27 | ||||
| -rw-r--r-- | internal/cryptocore/hkdf.go | 21 | 
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 +} | 
