cryptocore: add support for GCM-SIV
This commit is contained in:
parent
d1762c5b95
commit
7f87ed78f2
@ -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)
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
gcm = stupidgcm.New(key)
|
||||
} else {
|
||||
gcm, err = goGCMWrapper(blockCipher, IVLen)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
switch backend {
|
||||
case BackendOpenSSL:
|
||||
if IVLen != 16 {
|
||||
panic("stupidgcm only supports 128-bit IVs")
|
||||
}
|
||||
gcm = stupidgcm.New(key)
|
||||
case BackendGoGCM:
|
||||
gcm, err = goGCMWrapper(blockCipher, IVLen)
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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?
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
20
main.go
20
main.go
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user