diff options
| author | Jakob Unterwurzacher | 2016-09-20 21:58:04 +0200 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2016-09-25 16:43:17 +0200 | 
| commit | 7f87ed78f2f27831f2fa9409106846e3288c6f6e (patch) | |
| tree | b756236a7a9fcfce9f20eeee5ce4ba7aa51b00d9 | |
| parent | d1762c5b95c3279b0a2dfa3df5c99fe59922b666 (diff) | |
cryptocore: add support for GCM-SIV
| -rw-r--r-- | internal/configfile/config_file.go | 4 | ||||
| -rw-r--r-- | internal/configfile/feature_flags.go | 4 | ||||
| -rw-r--r-- | internal/contentenc/content.go | 8 | ||||
| -rw-r--r-- | internal/contentenc/content_test.go | 6 | ||||
| -rw-r--r-- | internal/cryptocore/cryptocore.go | 48 | ||||
| -rw-r--r-- | internal/cryptocore/cryptocore_go1.4_test.go | 5 | ||||
| -rw-r--r-- | internal/cryptocore/cryptocore_go1.5_test.go | 2 | ||||
| -rw-r--r-- | internal/cryptocore/cryptocore_test.go | 14 | ||||
| -rw-r--r-- | internal/fusefrontend/args.go | 6 | ||||
| -rw-r--r-- | internal/fusefrontend/fs.go | 2 | ||||
| -rw-r--r-- | internal/fusefrontend_reverse/rfs.go | 2 | ||||
| -rw-r--r-- | main.go | 20 | 
12 files changed, 75 insertions, 46 deletions
| diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index b36980f..178890b 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -139,7 +139,7 @@ func LoadConfFile(filename string, password string) ([]byte, *ConfFile, error) {  	// Unlock master key using password-based key  	// We use stock go GCM instead of OpenSSL here as we only use 96-bit IVs,  	// speed is not important and we get better error messages -	cc := cryptocore.New(scryptHash, false, false) +	cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, 96)  	ce := contentenc.New(cc, 4096)  	tlog.Warn.Enabled = false // Silence DecryptBlock() error messages on incorrect password @@ -163,7 +163,7 @@ func (cf *ConfFile) EncryptKey(key []byte, password string, logN int) {  	scryptHash := cf.ScryptObject.DeriveKey(password)  	// Lock master key using password-based key -	cc := cryptocore.New(scryptHash, false, false) +	cc := cryptocore.New(scryptHash, cryptocore.BackendGoGCM, 96)  	ce := contentenc.New(cc, 4096)  	cf.EncryptedKey = ce.EncryptBlock(key, 0, nil)  } diff --git a/internal/configfile/feature_flags.go b/internal/configfile/feature_flags.go index bac8ce8..90b8c22 100644 --- a/internal/configfile/feature_flags.go +++ b/internal/configfile/feature_flags.go @@ -8,6 +8,7 @@ const (  	FlagEMENames  	FlagGCMIV128  	FlagLongNames +	FlagGCMSIV  )  // knownFlags stores the known feature flags and their string representation @@ -17,6 +18,7 @@ var knownFlags map[flagIota]string = map[flagIota]string{  	FlagEMENames:       "EMENames",  	FlagGCMIV128:       "GCMIV128",  	FlagLongNames:      "LongNames", +	FlagGCMSIV:         "GCMSIV",  }  // Filesystems that do not have these feature flags set are deprecated. @@ -27,7 +29,7 @@ var requiredFlagsNormal []flagIota = []flagIota{  }  // Filesystems without filename encryption obviously don't have or need the -// related feature flags. +// filename related feature flags.  var requiredFlagsPlaintextNames []flagIota = []flagIota{  	FlagGCMIV128,  } diff --git a/internal/contentenc/content.go b/internal/contentenc/content.go index 375221a..e132536 100644 --- a/internal/contentenc/content.go +++ b/internal/contentenc/content.go @@ -14,6 +14,8 @@ import (  const (  	// Default plaintext block size  	DefaultBS = 4096 +	// We always use 128-bit IVs for file content encryption +	IVBitLen = 128  )  type ContentEnc struct { @@ -100,7 +102,7 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileId []b  	aData := make([]byte, 8)  	aData = append(aData, fileId...)  	binary.BigEndian.PutUint64(aData, blockNo) -	plaintext, err := be.cryptoCore.Gcm.Open(plaintext, nonce, ciphertext, aData) +	plaintext, err := be.cryptoCore.AEADCipher.Open(plaintext, nonce, ciphertext, aData)  	if err != nil {  		tlog.Warn.Printf("DecryptBlock: %s, len=%d", err.Error(), len(ciphertextOrig)) @@ -133,7 +135,7 @@ func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []by  	}  	// Get fresh nonce -	nonce := be.cryptoCore.GcmIVGen.Get() +	nonce := be.cryptoCore.IVGenerator.Get()  	// Authenticate block with block number and file ID  	aData := make([]byte, 8) @@ -141,7 +143,7 @@ func (be *ContentEnc) EncryptBlock(plaintext []byte, blockNo uint64, fileID []by  	aData = append(aData, fileID...)  	// Encrypt plaintext and append to nonce -	ciphertext := be.cryptoCore.Gcm.Seal(nonce, nonce, plaintext, aData) +	ciphertext := be.cryptoCore.AEADCipher.Seal(nonce, nonce, plaintext, aData)  	return ciphertext  } diff --git a/internal/contentenc/content_test.go b/internal/contentenc/content_test.go index 299c8c8..faa2780 100644 --- a/internal/contentenc/content_test.go +++ b/internal/contentenc/content_test.go @@ -23,7 +23,7 @@ func TestSplitRange(t *testing.T) {  		testRange{6654, 8945})  	key := make([]byte, cryptocore.KeyLen) -	cc := cryptocore.New(key, true, true) +	cc := cryptocore.New(key, cryptocore.BackendOpenSSL, IVBitLen)  	f := New(cc, DefaultBS)  	for _, r := range ranges { @@ -51,7 +51,7 @@ func TestCiphertextRange(t *testing.T) {  		testRange{6654, 8945})  	key := make([]byte, cryptocore.KeyLen) -	cc := cryptocore.New(key, true, true) +	cc := cryptocore.New(key, cryptocore.BackendOpenSSL, IVBitLen)  	f := New(cc, DefaultBS)  	for _, r := range ranges { @@ -74,7 +74,7 @@ func TestCiphertextRange(t *testing.T) {  func TestBlockNo(t *testing.T) {  	key := make([]byte, cryptocore.KeyLen) -	cc := cryptocore.New(key, true, true) +	cc := cryptocore.New(key, cryptocore.BackendOpenSSL, IVBitLen)  	f := New(cc, DefaultBS)  	b := f.CipherOffToBlockNo(788) diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index 1839aa2..a6708bd 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -8,17 +8,29 @@ import (  	"fmt"  	"github.com/rfjakob/gocryptfs/internal/stupidgcm" + +	"github.com/rfjakob/gcmsiv"  ) +type BackendTypeEnum int +  const (  	KeyLen     = 32 // AES-256  	AuthTagLen = 16 + +	_                              = iota // Skip zero +	BackendOpenSSL BackendTypeEnum = iota +	BackendGoGCM   BackendTypeEnum = iota +	BackendGCMSIV  BackendTypeEnum = iota  )  type CryptoCore struct { +	// AES-256 block cipher. This is used for EME filename encryption.  	BlockCipher cipher.Block -	Gcm         cipher.AEAD -	GcmIVGen    *nonceGenerator +	// GCM or GCM-SIV. This is used for content encryption. +	AEADCipher cipher.AEAD +	// GCM needs unique IVs (nonces) +	IVGenerator *nonceGenerator  	IVLen       int  } @@ -27,17 +39,12 @@ type CryptoCore struct {  // Even though the "GCMIV128" feature flag is now mandatory, we must still  // support 96-bit IVs here because they are used for encrypting the master  // key in gocryptfs.conf. -func New(key []byte, useOpenssl bool, GCMIV128 bool) *CryptoCore { - +func New(key []byte, backend BackendTypeEnum, IVBitLen int) *CryptoCore {  	if len(key) != KeyLen {  		panic(fmt.Sprintf("Unsupported key length %d", len(key)))  	} -  	// We want the IV size in bytes -	IVLen := 96 / 8 -	if GCMIV128 { -		IVLen = 128 / 8 -	} +	IVLen := IVBitLen / 8  	// Name encryption always uses built-in Go AES through BlockCipher.  	// Content encryption uses BlockCipher only if useOpenssl=false. @@ -47,20 +54,27 @@ func New(key []byte, useOpenssl bool, GCMIV128 bool) *CryptoCore {  	}  	var gcm cipher.AEAD -	if useOpenssl && GCMIV128 { -		// stupidgcm only supports 128-bit IVs +	switch backend { +	case BackendOpenSSL: +		if IVLen != 16 { +			panic("stupidgcm only supports 128-bit IVs") +		}  		gcm = stupidgcm.New(key) -	} else { +	case BackendGoGCM:  		gcm, err = goGCMWrapper(blockCipher, IVLen) -		if err != nil { -			panic(err) -		} +	case BackendGCMSIV: +		gcm, err = gcmsiv.NewGCMSIV(key) +	default: +		panic("unknown backend cipher") +	} +	if err != nil { +		panic(err)  	}  	return &CryptoCore{  		BlockCipher: blockCipher, -		Gcm:         gcm, -		GcmIVGen:    &nonceGenerator{nonceLen: IVLen}, +		AEADCipher:  gcm, +		IVGenerator: &nonceGenerator{nonceLen: IVLen},  		IVLen:       IVLen,  	}  } diff --git a/internal/cryptocore/cryptocore_go1.4_test.go b/internal/cryptocore/cryptocore_go1.4_test.go index 3460d02..14e1e03 100644 --- a/internal/cryptocore/cryptocore_go1.4_test.go +++ b/internal/cryptocore/cryptocore_go1.4_test.go @@ -7,7 +7,8 @@ import (  	"testing"  ) -// Native Go crypto with 128-bit IVs is only supported on Go 1.5 and up +// Native Go crypto with 128-bit IVs is only supported on Go 1.5 and up, +// this should panic.  func TestCryptoCoreNewGo14(t *testing.T) {  	defer func() {  		if r := recover(); r == nil { @@ -15,5 +16,5 @@ func TestCryptoCoreNewGo14(t *testing.T) {  		}  	}()  	key := make([]byte, 32) -	New(key, false, true) +	New(key, BackendGoGCM, 128)  } diff --git a/internal/cryptocore/cryptocore_go1.5_test.go b/internal/cryptocore/cryptocore_go1.5_test.go index 1c93254..f9d38e9 100644 --- a/internal/cryptocore/cryptocore_go1.5_test.go +++ b/internal/cryptocore/cryptocore_go1.5_test.go @@ -9,7 +9,7 @@ import (  func TestCryptoCoreNewGo15(t *testing.T) {  	key := make([]byte, 32) -	c := New(key, false, true) +	c := New(key, BackendGoGCM, 128)  	if c.IVLen != 16 {  		t.Fail()  	} diff --git a/internal/cryptocore/cryptocore_test.go b/internal/cryptocore/cryptocore_test.go index 1151591..9da6059 100644 --- a/internal/cryptocore/cryptocore_test.go +++ b/internal/cryptocore/cryptocore_test.go @@ -4,23 +4,19 @@ import (  	"testing"  ) -// "New" should accept all param combinations +// "New" should accept at least these param combinations  func TestCryptoCoreNew(t *testing.T) {  	key := make([]byte, 32) -	c := New(key, true, true) +	c := New(key, BackendOpenSSL, 128)  	if c.IVLen != 16 {  		t.Fail()  	} -	c = New(key, true, false) +	c = New(key, BackendGoGCM, 96)  	if c.IVLen != 12 {  		t.Fail()  	} -	c = New(key, false, false) -	if c.IVLen != 12 { -		t.Fail() -	} -	// "New(key, false, true)" is tested for Go 1.4 and 1.5+ seperately +	// "New(key, BackendGoGCM, 128)" is tested for Go 1.4 and 1.5+ seperately  }  // "New" should panic on any key not 32 bytes long @@ -32,5 +28,5 @@ func TestNewPanic(t *testing.T) {  	}()  	key := make([]byte, 16) -	New(key, true, true) +	New(key, BackendOpenSSL, 128)  } diff --git a/internal/fusefrontend/args.go b/internal/fusefrontend/args.go index 78b9b5b..d0e1835 100644 --- a/internal/fusefrontend/args.go +++ b/internal/fusefrontend/args.go @@ -1,10 +1,14 @@  package fusefrontend +import ( +	"github.com/rfjakob/gocryptfs/internal/cryptocore" +) +  // Container for arguments that are passed from main() to fusefrontend  type Args struct {  	Masterkey      []byte  	Cipherdir      string -	OpenSSL        bool +	CryptoBackend  cryptocore.BackendTypeEnum  	PlaintextNames bool  	LongNames      bool  	// Should we chown a file after it has been created? diff --git a/internal/fusefrontend/fs.go b/internal/fusefrontend/fs.go index bc81c37..575865e 100644 --- a/internal/fusefrontend/fs.go +++ b/internal/fusefrontend/fs.go @@ -37,7 +37,7 @@ type FS struct {  // Encrypted FUSE overlay filesystem  func NewFS(args Args) *FS { -	cryptoCore := cryptocore.New(args.Masterkey, args.OpenSSL, true) +	cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.IVBitLen)  	contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)  	nameTransform := nametransform.New(cryptoCore, args.LongNames) diff --git a/internal/fusefrontend_reverse/rfs.go b/internal/fusefrontend_reverse/rfs.go index e20c851..4b04b86 100644 --- a/internal/fusefrontend_reverse/rfs.go +++ b/internal/fusefrontend_reverse/rfs.go @@ -36,7 +36,7 @@ type reverseFS struct {  // Encrypted FUSE overlay filesystem  func NewFS(args fusefrontend.Args) *reverseFS { -	cryptoCore := cryptocore.New(args.Masterkey, args.OpenSSL, true) +	cryptoCore := cryptocore.New(args.Masterkey, args.CryptoBackend, contentenc.IVBitLen)  	contentEnc := contentenc.New(cryptoCore, contentenc.DefaultBS)  	nameTransform := nametransform.New(cryptoCore, args.LongNames) @@ -282,20 +282,30 @@ func main() {  // initFuseFrontend - initialize gocryptfs/fusefrontend  // Calls os.Exit on errors  func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFile) *fuse.Server { - -	// Reconciliate CLI and config file arguments into a Args struct that is passed to the -	// filesystem implementation +	// Reconciliate CLI and config file arguments into a fusefrontend.Args struct +	// that is passed to the filesystem implementation +	cryptoBackend := cryptocore.BackendGoGCM +	if args.openssl { +		cryptoBackend = cryptocore.BackendOpenSSL +	} +	if args.reverse { +		// reverse implies GCMSIV +		cryptoBackend = cryptocore.BackendGCMSIV +	}  	frontendArgs := fusefrontend.Args{  		Cipherdir:      args.cipherdir,  		Masterkey:      key, -		OpenSSL:        args.openssl,  		PlaintextNames: args.plaintextnames,  		LongNames:      args.longnames, +		CryptoBackend:  cryptoBackend,  	}  	// confFile is nil when "-zerokey" or "-masterkey" was used  	if confFile != nil {  		// Settings from the config file override command line args  		frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(configfile.FlagPlaintextNames) +		if confFile.IsFeatureFlagSet(configfile.FlagGCMSIV) { +			frontendArgs.CryptoBackend = cryptocore.BackendGCMSIV +		}  	}  	// If allow_other is set and we run as root, try to give newly created files to  	// the right user. @@ -308,7 +318,7 @@ func initFuseFrontend(key []byte, args argContainer, confFile *configfile.ConfFi  	var finalFs pathfs.FileSystem  	if args.reverse {  		finalFs = fusefrontend_reverse.NewFS(frontendArgs) -		tlog.Info.Printf(tlog.ColorYellow + "REVERSE MODE IS EXPERIMENTAL" + tlog.ColorReset) +		tlog.Info.Printf(tlog.ColorYellow + "REVERSE MODE IS EXPERIMENTAL!" + tlog.ColorReset)  	} else {  		finalFs = fusefrontend.NewFS(frontendArgs)  	} | 
