From 779a850e0fb967aac79124c7e18b14706d5f2652 Mon Sep 17 00:00:00 2001 From: Frank Denis Date: Tue, 25 Feb 2025 15:03:50 +0100 Subject: Add optional support for AEGIS encryption AEGIS is a new family of authenticated encryption algorithms that offers stronger security, higher usage limits, and better performance than AES-GCM. This pull request adds support for a new `-aegis` command-line flag, allowing AEGIS-128X2 to be used as an alternative to AES-GCM on CPUs with AES acceleration. It also introduces the ability to use ciphers with different key sizes. More information on AEGIS is available here: - https://cfrg.github.io/draft-irtf-cfrg-aegis-aead/draft-irtf-cfrg-aegis-aead.html - https://github.com/cfrg/draft-irtf-cfrg-aegis-aead gocryptfs -speed speed on Apple M1: AES-GCM-256-OpenSSL 3718.79 MB/s AES-GCM-256-Go 5083.43 MB/s (selected in auto mode) AES-SIV-512-Go 625.20 MB/s XChaCha20-Poly1305-OpenSSL 1358.63 MB/s (selected in auto mode) XChaCha20-Poly1305-Go 832.11 MB/s Aegis128X2-Go 11818.73 MB/s gocryptfs -speed speed on AMD Zen 4: AES-GCM-256-OpenSSL 5215.86 MB/s AES-GCM-256-Go 6918.01 MB/s (selected in auto mode) AES-SIV-512-Go 449.61 MB/s XChaCha20-Poly1305-OpenSSL 2643.48 MB/s XChaCha20-Poly1305-Go 3727.46 MB/s (selected in auto mode) Aegis128X2-Go 28109.92 MB/s --- internal/cryptocore/cryptocore.go | 48 +++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 10 deletions(-) (limited to 'internal/cryptocore') diff --git a/internal/cryptocore/cryptocore.go b/internal/cryptocore/cryptocore.go index 72c9c23..0848096 100644 --- a/internal/cryptocore/cryptocore.go +++ b/internal/cryptocore/cryptocore.go @@ -11,6 +11,7 @@ import ( "golang.org/x/crypto/chacha20poly1305" + "github.com/aegis-aead/go-libaegis/aegis128x2" "github.com/rfjakob/eme" "github.com/rfjakob/gocryptfs/v2/internal/siv_aead" @@ -19,11 +20,13 @@ import ( ) const ( - // KeyLen is the cipher key length in bytes. All backends use 32 bytes. - KeyLen = 32 // AuthTagLen is the length of a authentication tag in bytes. // All backends use 16 bytes. AuthTagLen = 16 + // Minimum key length + MinKeyLen = 16 + // Maximum key length + MaxKeyLen = 32 ) // AEADTypeEnum indicates the type of AEAD backend in use. @@ -32,6 +35,7 @@ type AEADTypeEnum struct { Algo string // Lib is the library where Algo is implemented. Either "Go" or "OpenSSL". Lib string + KeyLen int NonceSize int } @@ -42,22 +46,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", 16} +var BackendOpenSSL = AEADTypeEnum{"AES-GCM-256", "OpenSSL", 32, 16} // BackendGoGCM specifies the Go based AES-256-GCM backend. // "AES-GCM-256-Go" in gocryptfs -speed. -var BackendGoGCM = AEADTypeEnum{"AES-GCM-256", "Go", 16} +var BackendGoGCM = AEADTypeEnum{"AES-GCM-256", "Go", 32, 16} // BackendAESSIV specifies an AESSIV backend. // "AES-SIV-512-Go" in gocryptfs -speed. -var BackendAESSIV = AEADTypeEnum{"AES-SIV-512", "Go", siv_aead.NonceSize} +var BackendAESSIV = AEADTypeEnum{"AES-SIV-512", "Go", 32, siv_aead.NonceSize} // BackendXChaCha20Poly1305 specifies XChaCha20-Poly1305-Go. // "XChaCha20-Poly1305-Go" in gocryptfs -speed. -var BackendXChaCha20Poly1305 = AEADTypeEnum{"XChaCha20-Poly1305", "Go", chacha20poly1305.NonceSizeX} +var BackendXChaCha20Poly1305 = AEADTypeEnum{"XChaCha20-Poly1305", "Go", 32, chacha20poly1305.NonceSizeX} // BackendXChaCha20Poly1305OpenSSL specifies XChaCha20-Poly1305-OpenSSL. -var BackendXChaCha20Poly1305OpenSSL = AEADTypeEnum{"XChaCha20-Poly1305", "OpenSSL", chacha20poly1305.NonceSizeX} +var BackendXChaCha20Poly1305OpenSSL = AEADTypeEnum{"XChaCha20-Poly1305", "OpenSSL", 32, chacha20poly1305.NonceSizeX} + +var BackendAegis = AEADTypeEnum{"Aegis128X2", "Go", 16, aegis128x2.NonceSize} // CryptoCore is the low level crypto implementation. type CryptoCore struct { @@ -85,9 +91,14 @@ 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) - if len(key) != KeyLen { + keyLen := aeadType.KeyLen + if keyLen < MinKeyLen || keyLen > MaxKeyLen { + log.Panicf("Unsupported key length of %d bytes", keyLen) + } + if len(key) < keyLen { log.Panicf("Unsupported key length of %d bytes", len(key)) } + key = key[:keyLen] // keys can safely be truncated if IVBitLen != 96 && IVBitLen != 128 && IVBitLen != chacha20poly1305.NonceSizeX*8 { log.Panicf("Unsupported IV length of %d bits", IVBitLen) } @@ -98,7 +109,7 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoC { var emeBlockCipher cipher.Block if useHKDF { - emeKey := hkdfDerive(key, hkdfInfoEMENames, KeyLen) + emeKey := hkdfDerive(key, hkdfInfoEMENames, keyLen) emeBlockCipher, err = aes.NewCipher(emeKey) for i := range emeKey { emeKey[i] = 0 @@ -117,7 +128,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 @@ -183,6 +194,23 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool) *CryptoC if err != nil { log.Panic(err) } + } else if aeadType == BackendAegis { + if stupidgcm.BuiltWithoutAegis { + log.Panic("AEGIS is not available") + } + if IVBitLen != 128 { + log.Panicf("AEGIS must use 128-bit IVs, you wanted %d", IVBitLen) + } + var aegisKey []byte + if useHKDF { + aegisKey = hkdfDerive(key, hkdfInfoGCMContent, keyLen) + } else { + aegisKey = append([]byte{}, key...) + } + aeadCipher = stupidgcm.NewAegis(aegisKey) + for i := range aegisKey { + aegisKey[i] = 0 + } } else { log.Panicf("unknown cipher backend %q", aeadType) } -- cgit v1.2.3