From 3e852eb354f99fb95b399c68d950298b33ed88ab Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Wed, 12 Mar 2025 00:32:04 +0100 Subject: Do what @rfjakob asked me to do --- Documentation/MANPAGE.md | 2 +- Documentation/file-format.md | 2 +- cli_args.go | 2 +- gocryptfs-xray/xray_main.go | 2 +- internal/configfile/config_file.go | 6 ++-- internal/configfile/feature_flags.go | 6 ++-- internal/configfile/scrypt.go | 8 +++--- internal/configfile/validate.go | 7 +++-- internal/contentenc/content_test.go | 9 ++---- internal/contentenc/offsets_test.go | 3 +- internal/cryptocore/cryptocore.go | 47 +++++++++++--------------------- internal/fusefrontend/xattr_unit_test.go | 3 +- internal/nametransform/longnames_test.go | 3 +- internal/speed/speed.go | 6 +++- internal/stupidgcm/aegis_test.go | 16 ----------- masterkey.go | 6 ++-- 16 files changed, 49 insertions(+), 79 deletions(-) delete mode 100644 internal/stupidgcm/aegis_test.go diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md index fd8f5db..2f5cf53 100644 --- a/Documentation/MANPAGE.md +++ b/Documentation/MANPAGE.md @@ -165,7 +165,7 @@ Use XChaCha20-Poly1305 file content encryption. This should be much faster than AES-GCM on CPUs that lack AES acceleration. #### -aegis -Use AEGIS file content encryption. This should be much faster +Use AEGIS256X2 file content encryption. This should be much faster than AES-GCM on CPUs with AES acceleration. Run `gocryptfs -speed` to find out if and how much faster. diff --git a/Documentation/file-format.md b/Documentation/file-format.md index ee10524..73c505f 100644 --- a/Documentation/file-format.md +++ b/Documentation/file-format.md @@ -26,7 +26,7 @@ Data block, XChaCha20-Poly1305 (enabled via `-init -xchacha`) Data block, AEGIS (enabled via `-init -aegis`) - 16 bytes nonce + 32 bytes nonce 1-4096 bytes encrypted data 16 bytes tag diff --git a/cli_args.go b/cli_args.go index 26ebdd1..5e8f892 100644 --- a/cli_args.go +++ b/cli_args.go @@ -188,7 +188,7 @@ func parseCliOpts(osArgs []string) (args argContainer) { flagSet.BoolVar(&args.one_file_system, "one-file-system", false, "Don't cross filesystem boundaries") flagSet.BoolVar(&args.deterministic_names, "deterministic-names", false, "Disable diriv file name randomisation") flagSet.BoolVar(&args.xchacha, "xchacha", false, "Use XChaCha20-Poly1305 file content encryption") - flagSet.BoolVar(&args.aegis, "aegis", false, "Use AEGIS file content encryption") + flagSet.BoolVar(&args.aegis, "aegis", false, "Use AEGIS-256X2 file content encryption") // Mount options with opposites flagSet.BoolVar(&args.dev, "dev", false, "Allow device files") diff --git a/gocryptfs-xray/xray_main.go b/gocryptfs-xray/xray_main.go index efba752..2e90501 100644 --- a/gocryptfs-xray/xray_main.go +++ b/gocryptfs-xray/xray_main.go @@ -95,7 +95,7 @@ func main() { args.sep0 = flag.Bool("0", false, "Use \\0 instead of \\n as separator") args.aessiv = flag.Bool("aessiv", false, "Assume AES-SIV mode instead of AES-GCM") args.xchacha = flag.Bool("xchacha", false, "Assume XChaCha20-Poly1305 mode instead of AES-GCM") - args.aegis = flag.Bool("aegis", false, "Assume AEGIS mode instead of AES-GCM") + args.aegis = flag.Bool("aegis", false, "Assume AEGIS-256X2 mode instead of AES-GCM") args.fido2 = flag.String("fido2", "", "Protect the masterkey using a FIDO2 token instead of a password") args.version = flag.Bool("version", false, "Print version information") diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go index 5e10228..60b8a15 100644 --- a/internal/configfile/config_file.go +++ b/internal/configfile/config_file.go @@ -94,7 +94,7 @@ func Create(args *CreateArgs) error { if args.XChaCha20Poly1305 { cf.setFeatureFlag(FlagXChaCha20Poly1305) } else if args.Aegis { - cf.setFeatureFlag(FlagAegis) + cf.setFeatureFlag(FlagAegis256X2) } else { // 128-bit IVs are mandatory for AES-GCM (default is 96!) and AES-SIV, // XChaCha20Poly1305 uses even an even longer IV of 192 bits. @@ -136,7 +136,7 @@ func Create(args *CreateArgs) error { key := args.Masterkey if key == nil { // Generate new random master key - key = cryptocore.RandBytes(cryptocore.MaxKeyLen) + key = cryptocore.RandBytes(cryptocore.KeyLen) } tlog.PrintMasterkeyReminder(key) // Encrypt it using the password @@ -330,7 +330,7 @@ func (cf *ConfFile) ContentEncryption() (algo cryptocore.AEADTypeEnum, err error if cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) { return cryptocore.BackendXChaCha20Poly1305, nil } - if cf.IsFeatureFlagSet(FlagAegis) { + if cf.IsFeatureFlagSet(FlagAegis256X2) { return cryptocore.BackendAegis, nil } if cf.IsFeatureFlagSet(FlagAESSIV) { diff --git a/internal/configfile/feature_flags.go b/internal/configfile/feature_flags.go index 2722831..b4cc611 100644 --- a/internal/configfile/feature_flags.go +++ b/internal/configfile/feature_flags.go @@ -34,8 +34,8 @@ const ( FlagFIDO2 // FlagXChaCha20Poly1305 means we use XChaCha20-Poly1305 file content encryption FlagXChaCha20Poly1305 - // FlagAegis means we use Aegis file content encryption - FlagAegis + // FlagAegis256X2 means we use Aegis256X2 file content encryption + FlagAegis256X2 ) // knownFlags stores the known feature flags and their string representation @@ -51,7 +51,7 @@ var knownFlags = map[flagIota]string{ FlagHKDF: "HKDF", FlagFIDO2: "FIDO2", FlagXChaCha20Poly1305: "XChaCha20Poly1305", - FlagAegis: "AEGIS", + FlagAegis256X2: "AEGIS256X2", } // isFeatureFlagKnown verifies that we understand a feature flag. diff --git a/internal/configfile/scrypt.go b/internal/configfile/scrypt.go index b82a431..0ce8777 100644 --- a/internal/configfile/scrypt.go +++ b/internal/configfile/scrypt.go @@ -49,7 +49,7 @@ type ScryptKDF struct { // NewScryptKDF returns a new instance of ScryptKDF. func NewScryptKDF(logN int) ScryptKDF { var s ScryptKDF - s.Salt = cryptocore.RandBytes(cryptocore.MaxKeyLen) + s.Salt = cryptocore.RandBytes(cryptocore.KeyLen) if logN <= 0 { s.N = 1 << ScryptDefaultLogN } else { @@ -57,7 +57,7 @@ func NewScryptKDF(logN int) ScryptKDF { } s.R = 8 // Always 8 s.P = 1 // Always 1 - s.KeyLen = cryptocore.MaxKeyLen + s.KeyLen = cryptocore.KeyLen return s } @@ -98,8 +98,8 @@ func (s *ScryptKDF) validateParams() error { if len(s.Salt) < scryptMinSaltLen { return fmt.Errorf("Fatal: scrypt salt length below minimum: value=%d, min=%d", len(s.Salt), scryptMinSaltLen) } - if s.KeyLen < cryptocore.MinKeyLen { - return fmt.Errorf("Fatal: scrypt parameter KeyLen below minimum: value=%d, min=%d", s.KeyLen, cryptocore.MinKeyLen) + if s.KeyLen < cryptocore.KeyLen { + return fmt.Errorf("Fatal: scrypt parameter KeyLen below minimum: value=%d, min=%d", s.KeyLen, cryptocore.KeyLen) } return nil } diff --git a/internal/configfile/validate.go b/internal/configfile/validate.go index 333eea6..ad3728f 100644 --- a/internal/configfile/validate.go +++ b/internal/configfile/validate.go @@ -38,13 +38,16 @@ func (cf *ConfFile) Validate() error { return fmt.Errorf("XChaCha20Poly1305 requires HKDF feature flag") } } - if cf.IsFeatureFlagSet(FlagAegis) { + if cf.IsFeatureFlagSet(FlagAegis256X2) { if cf.IsFeatureFlagSet(FlagGCMIV128) { return fmt.Errorf("AEGIS conflicts with GCMIV128 feature flag") } + if cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) { + return fmt.Errorf("AEGIS conflicts with XChaCha20Poly1305 feature flag") + } } // The absence of other flags means AES-GCM (oldest algorithm) - if !cf.IsFeatureFlagSet(FlagAegis) && !cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) && !cf.IsFeatureFlagSet(FlagAESSIV) { + if !cf.IsFeatureFlagSet(FlagAegis256X2) && !cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) && !cf.IsFeatureFlagSet(FlagAESSIV) { if !cf.IsFeatureFlagSet(FlagGCMIV128) { return fmt.Errorf("AES-GCM requires GCMIV128 feature flag") } diff --git a/internal/contentenc/content_test.go b/internal/contentenc/content_test.go index 2a34307..b20ccb1 100644 --- a/internal/contentenc/content_test.go +++ b/internal/contentenc/content_test.go @@ -22,8 +22,7 @@ func TestSplitRange(t *testing.T) { {6654, 8945}, } - keyLen := cryptocore.BackendGoGCM.KeyLen - key := make([]byte, keyLen) + key := make([]byte, cryptocore.KeyLen) cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true) f := New(cc, DefaultBS) @@ -51,8 +50,7 @@ func TestCiphertextRange(t *testing.T) { {6654, 8945}, } - keyLen := cryptocore.BackendGoGCM.KeyLen - key := make([]byte, keyLen) + key := make([]byte, cryptocore.KeyLen) cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true) f := New(cc, DefaultBS) @@ -75,8 +73,7 @@ func TestCiphertextRange(t *testing.T) { } func TestBlockNo(t *testing.T) { - keyLen := cryptocore.BackendGoGCM.KeyLen - key := make([]byte, keyLen) + key := make([]byte, cryptocore.KeyLen) cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true) f := New(cc, DefaultBS) diff --git a/internal/contentenc/offsets_test.go b/internal/contentenc/offsets_test.go index 0118c5e..b35964a 100644 --- a/internal/contentenc/offsets_test.go +++ b/internal/contentenc/offsets_test.go @@ -9,8 +9,7 @@ import ( // TestSizeToSize tests CipherSizeToPlainSize and PlainSizeToCipherSize func TestSizeToSize(t *testing.T) { - keyLen := cryptocore.BackendGoGCM.KeyLen - key := make([]byte, keyLen) + key := make([]byte, cryptocore.KeyLen) cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true) ce := New(cc, DefaultBS) diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index 0fd401f..b1533db 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -11,7 +11,7 @@ import ( "golang.org/x/crypto/chacha20poly1305" - "github.com/aegis-aead/go-libaegis/aegis128x2" + "github.com/aegis-aead/go-libaegis/aegis256x2" "github.com/rfjakob/eme" "github.com/rfjakob/gocryptfs/v2/internal/siv_aead" @@ -23,14 +23,8 @@ const ( // AuthTagLen is the length of a authentication tag in bytes. // All backends use 16 bytes. AuthTagLen = 16 - // EME key length - EMEKeyLen = 32 - // Key length for key derivation, or directly for ciphers when not using HKDF - KDFKeyLen = 32 - // Minimum AEAD key length - MinKeyLen = 16 - // Maximum AEAD key length - MaxKeyLen = 32 + // AEAD key length + KeyLen = 32 ) // AEADTypeEnum indicates the type of AEAD backend in use. @@ -39,7 +33,6 @@ type AEADTypeEnum struct { Algo string // Lib is the library where Algo is implemented. Either "Go" or "OpenSSL". Lib string - KeyLen int NonceSize int } @@ -50,24 +43,24 @@ func (a AEADTypeEnum) String() string { // BackendOpenSSL specifies the OpenSSL AES-256-GCM backend. // "AES-GCM-256-OpenSSL" in gocryptfs -speed. -var BackendOpenSSL = AEADTypeEnum{"AES-GCM-256", "OpenSSL", 32, 16} +var BackendOpenSSL = AEADTypeEnum{"AES-GCM-256", "OpenSSL", 16} // BackendGoGCM specifies the Go based AES-256-GCM backend. // "AES-GCM-256-Go" in gocryptfs -speed. -var BackendGoGCM = AEADTypeEnum{"AES-GCM-256", "Go", 32, 16} +var BackendGoGCM = AEADTypeEnum{"AES-GCM-256", "Go", 16} // BackendAESSIV specifies an AESSIV backend. // "AES-SIV-512-Go" in gocryptfs -speed. -var BackendAESSIV = AEADTypeEnum{"AES-SIV-512", "Go", 32, siv_aead.NonceSize} +var BackendAESSIV = AEADTypeEnum{"AES-SIV-512", "Go", siv_aead.NonceSize} // BackendXChaCha20Poly1305 specifies XChaCha20-Poly1305-Go. // "XChaCha20-Poly1305-Go" in gocryptfs -speed. -var BackendXChaCha20Poly1305 = AEADTypeEnum{"XChaCha20-Poly1305", "Go", 32, chacha20poly1305.NonceSizeX} +var BackendXChaCha20Poly1305 = AEADTypeEnum{"XChaCha20-Poly1305", "Go", chacha20poly1305.NonceSizeX} // BackendXChaCha20Poly1305OpenSSL specifies XChaCha20-Poly1305-OpenSSL. -var BackendXChaCha20Poly1305OpenSSL = AEADTypeEnum{"XChaCha20-Poly1305", "OpenSSL", 32, chacha20poly1305.NonceSizeX} +var BackendXChaCha20Poly1305OpenSSL = AEADTypeEnum{"XChaCha20-Poly1305", "OpenSSL", chacha20poly1305.NonceSizeX} -var BackendAegis = AEADTypeEnum{"Aegis128X2", "Go", 16, aegis128x2.NonceSize} +var BackendAegis = AEADTypeEnum{"Aegis256X2", "Go", aegis256x2.NonceSize} // CryptoCore is the low level crypto implementation. type CryptoCore struct { @@ -95,12 +88,6 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoC tlog.Debug.Printf("cryptocore.New: key=%d bytes, aeadType=%v, IVBitLen=%d, useHKDF=%v", len(key), aeadType, IVBitLen, useHKDF) - keyLen := aeadType.KeyLen - if !useHKDF && len(key) != keyLen { - log.Panicf("Key length mismatch: got %d bytes, want %d bytes", len(key), keyLen) - } else if useHKDF && len(key) != KDFKeyLen { - log.Panicf("Key length mismatch: got %d bytes, want %d bytes for key derivation", len(key), KDFKeyLen) - } if IVBitLen != 96 && IVBitLen != 128 && IVBitLen != chacha20poly1305.NonceSizeX*8 { log.Panicf("Unsupported IV length of %d bits", IVBitLen) } @@ -111,7 +98,7 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoC { var emeBlockCipher cipher.Block if useHKDF { - emeKey := hkdfDerive(key, hkdfInfoEMENames, EMEKeyLen) + emeKey := hkdfDerive(key, hkdfInfoEMENames, KeyLen) emeBlockCipher, err = aes.NewCipher(emeKey) for i := range emeKey { emeKey[i] = 0 @@ -130,7 +117,7 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoC if aeadType == BackendOpenSSL || aeadType == BackendGoGCM { var gcmKey []byte if useHKDF { - gcmKey = hkdfDerive(key, hkdfInfoGCMContent, keyLen) + gcmKey = hkdfDerive(key, hkdfInfoGCMContent, KeyLen) } else { // Filesystems created by gocryptfs v0.7 through v1.2 don't use HKDF. // Example: tests/example_filesystems/v0.9 @@ -200,15 +187,13 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoC if stupidgcm.BuiltWithoutAegis { log.Panic("AEGIS is not available") } - if IVBitLen != 128 { - log.Panicf("AEGIS must use 128-bit IVs, you wanted %d", IVBitLen) + if IVBitLen != aegis256x2.NonceSize*8 { + log.Panicf("AEGIS-256X2 must use 256-bit IVs, you wanted %d", IVBitLen) } - var aegisKey []byte - if useHKDF { - aegisKey = hkdfDerive(key, hkdfInfoGCMContent, keyLen) - } else { - aegisKey = append([]byte{}, key...) + if !useHKDF { + log.Panic("XChaCha20-Poly1305 must use HKDF, but it is disabled") } + aegisKey := hkdfDerive(key, hkdfInfoGCMContent, KeyLen) aeadCipher = stupidgcm.NewAegis(aegisKey) for i := range aegisKey { aegisKey[i] = 0 diff --git a/internal/fusefrontend/xattr_unit_test.go b/internal/fusefrontend/xattr_unit_test.go index 5f69e00..86c87a7 100644 --- a/internal/fusefrontend/xattr_unit_test.go +++ b/internal/fusefrontend/xattr_unit_test.go @@ -16,8 +16,7 @@ import ( func newTestFS(args Args) *RootNode { // Init crypto backend - keyLen := cryptocore.BackendGoGCM.KeyLen - key := make([]byte, keyLen) + key := make([]byte, cryptocore.KeyLen) cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true) cEnc := contentenc.New(cCore, contentenc.DefaultBS) n := nametransform.New(cCore.EMECipher, true, 0, true, nil, false) diff --git a/internal/nametransform/longnames_test.go b/internal/nametransform/longnames_test.go index be18f92..7a4e915 100644 --- a/internal/nametransform/longnames_test.go +++ b/internal/nametransform/longnames_test.go @@ -34,8 +34,7 @@ func TestRemoveLongNameSuffix(t *testing.T) { } func newLognamesTestInstance(longNameMax uint8) *NameTransform { - keyLen := cryptocore.BackendGoGCM.KeyLen - key := make([]byte, keyLen) + key := make([]byte, cryptocore.KeyLen) cCore := cryptocore.New(key, cryptocore.BackendGoGCM, contentenc.DefaultIVBits, true) return New(cCore.EMECipher, true, longNameMax, true, nil, false) } diff --git a/internal/speed/speed.go b/internal/speed/speed.go index e60e37d..c54f487 100644 --- a/internal/speed/speed.go +++ b/internal/speed/speed.go @@ -14,6 +14,7 @@ import ( "golang.org/x/crypto/chacha20poly1305" + "github.com/aegis-aead/go-libaegis/common" "github.com/rfjakob/gocryptfs/v2/internal/cryptocore" "github.com/rfjakob/gocryptfs/v2/internal/siv_aead" "github.com/rfjakob/gocryptfs/v2/internal/stupidgcm" @@ -172,6 +173,9 @@ func bStupidXchacha(b *testing.B) { // bAegis benchmarks Aegis from github.com/aegis-aead/go-libaegis func bAegis(b *testing.B) { - c := stupidgcm.NewAegis(randBytes(16)) + if common.Available { + b.Skip("aegis is not available") + } + c := stupidgcm.NewAegis(randBytes(32)) bEncrypt(b, c) } diff --git a/internal/stupidgcm/aegis_test.go b/internal/stupidgcm/aegis_test.go deleted file mode 100644 index 36ef763..0000000 --- a/internal/stupidgcm/aegis_test.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build !without_aegis && cgo -// +build !without_aegis,cgo - -package stupidgcm - -import "testing" - -func TestStupidAegis(t *testing.T) { - if BuiltWithoutAegis { - t.Skip("Aegis support has been disabled at compile-time") - } - key := randBytes(16) - c := NewAegis(key) - - testCiphers(t, c, c) -} diff --git a/masterkey.go b/masterkey.go index 90f0adf..d488441 100644 --- a/masterkey.go +++ b/masterkey.go @@ -20,8 +20,8 @@ func unhexMasterKey(masterkey string, fromStdin bool) []byte { tlog.Fatal.Printf("Could not parse master key: %v", err) os.Exit(exitcodes.MasterKey) } - if len(key) != cryptocore.MaxKeyLen { - tlog.Fatal.Printf("Master key has length %d but we require length %d", len(key), cryptocore.MaxKeyLen) + if len(key) != cryptocore.KeyLen { + tlog.Fatal.Printf("Master key has length %d but we require length %d", len(key), cryptocore.KeyLen) os.Exit(exitcodes.MasterKey) } tlog.Info.Printf("Using explicit master key.") @@ -56,7 +56,7 @@ func handleArgsMasterkey(args *argContainer) (masterkey []byte) { tlog.Info.Printf(tlog.ColorYellow + "ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING." + tlog.ColorReset) - return make([]byte, cryptocore.MaxKeyLen) + return make([]byte, cryptocore.KeyLen) } // No master key source specified on the command line. Caller must parse // the config file. -- cgit v1.2.3