aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--Documentation/MANPAGE.md4
-rw-r--r--Documentation/file-format.md12
-rw-r--r--Makefile2
-rw-r--r--README.md2
-rwxr-xr-xbenchmark.bash7
-rwxr-xr-xbuild-without-aegis.bash10
-rwxr-xr-xbuild-without-cgo.bash10
-rwxr-xr-xbuild-without-openssl.bash10
-rw-r--r--cli_args.go5
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--gocryptfs-xray/xray_main.go4
-rw-r--r--init_dir.go1
-rw-r--r--internal/configfile/config_file.go16
-rw-r--r--internal/configfile/feature_flags.go3
-rw-r--r--internal/configfile/scrypt.go8
-rw-r--r--internal/configfile/validate.go7
-rw-r--r--internal/contentenc/content_test.go9
-rw-r--r--internal/contentenc/offsets_test.go3
-rw-r--r--internal/cryptocore/cryptocore.go48
-rw-r--r--internal/exitcodes/exitcodes.go2
-rw-r--r--internal/fusefrontend/xattr_unit_test.go3
-rw-r--r--internal/nametransform/longnames_test.go3
-rw-r--r--internal/speed/speed.go9
-rw-r--r--internal/stupidgcm/Makefile8
-rw-r--r--internal/stupidgcm/aegis.go57
-rw-r--r--internal/stupidgcm/aegis_test.go16
-rw-r--r--internal/stupidgcm/chacha.go4
-rw-r--r--internal/stupidgcm/chacha_test.go4
-rw-r--r--internal/stupidgcm/common.go4
-rw-r--r--internal/stupidgcm/common_test.go2
-rw-r--r--internal/stupidgcm/doc.go6
-rw-r--r--internal/stupidgcm/gcm.go4
-rw-r--r--internal/stupidgcm/gcm_test.go4
-rw-r--r--internal/stupidgcm/locking.go4
-rw-r--r--internal/stupidgcm/openssl.go4
-rw-r--r--internal/stupidgcm/without_aegis.go28
-rw-r--r--internal/stupidgcm/without_openssl.go4
-rw-r--r--internal/stupidgcm/xchacha.go4
-rw-r--r--internal/stupidgcm/xchacha_test.go4
-rw-r--r--masterkey.go6
-rw-r--r--mount.go3
-rwxr-xr-xpackage-release-tarballs.bash2
-rwxr-xr-xtest.bash4
-rw-r--r--tests/matrix/matrix_test.go9
46 files changed, 288 insertions, 76 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c29194a..60f4f76 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -47,7 +47,7 @@ jobs:
- run: echo -e 'user_allow_other\nmount_max = 10000' | sudo tee -a /etc/fuse.conf && cat /etc/fuse.conf
# Build & upload static binary
- - run: ./build-without-openssl.bash
+ - run: ./build-without-cgo.bash
- uses: actions/upload-artifact@v4
with:
name: gocryptfs ${{ github.sha }} static ${{ runner.arch }} binary, Go ${{ matrix.go }}
diff --git a/Documentation/MANPAGE.md b/Documentation/MANPAGE.md
index c7a1c03..fd8f5db 100644
--- a/Documentation/MANPAGE.md
+++ b/Documentation/MANPAGE.md
@@ -164,6 +164,10 @@ specify `-aessiv`.
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
+than AES-GCM on CPUs with AES acceleration.
+
Run `gocryptfs -speed` to find out if and how much faster.
MOUNT OPTIONS
diff --git a/Documentation/file-format.md b/Documentation/file-format.md
index 7cce72c..ee10524 100644
--- a/Documentation/file-format.md
+++ b/Documentation/file-format.md
@@ -24,7 +24,13 @@ Data block, XChaCha20-Poly1305 (enabled via `-init -xchacha`)
1-4096 bytes encrypted data
16 bytes Poly1305 tag
-Full block overhead (AES-GCM and AES-SIV mode) = 32/4096 = 1/128 = 0.78125 %
+Data block, AEGIS (enabled via `-init -aegis`)
+
+ 16 bytes nonce
+ 1-4096 bytes encrypted data
+ 16 bytes tag
+
+Full block overhead (AEGIS, AES-GCM and AES-SIV mode) = 32/4096 = 1/128 = 0.78125 %
Full block overhead (XChaCha20-Poly1305 mode) = 40/4096 = \~1 %
@@ -36,8 +42,8 @@ Example: 1-byte file, AES-GCM and AES-SIV mode
Total: 51 bytes
-Example: 5000-byte file, , AES-GCM and AES-SIV mode
----------------------------------------------------
+Example: 5000-byte file, AEGIS, AES-GCM and AES-SIV mode
+--------------------------------------------------------
Header 18 bytes
Data block 4128 bytes
diff --git a/Makefile b/Makefile
index beccc5f..48e2113 100644
--- a/Makefile
+++ b/Makefile
@@ -42,7 +42,7 @@ ci:
uname -a ; go version ; openssl version
df -Th / /tmp /var/tmp
- ./build-without-openssl.bash
+ ./build-without-cgo.bash
./build.bash
./test.bash
make root_test
diff --git a/README.md b/README.md
index 3776c01..14020bb 100644
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ Then, download the source code and compile:
$ git clone https://github.com/rfjakob/gocryptfs.git
$ cd gocryptfs
- $ ./build-without-openssl.bash
+ $ ./build-without-cgo.bash
This will compile a static binary that uses the Go stdlib crypto backend.
diff --git a/benchmark.bash b/benchmark.bash
index fb99c65..db962a7 100755
--- a/benchmark.bash
+++ b/benchmark.bash
@@ -17,6 +17,7 @@ OPT_OPENSSL=""
OPT_DIR=""
DD_ONLY=""
OPT_XCHACHA=""
+OPT_AEGIS=""
while [[ $# -gt 0 ]] ; do
case $1 in
@@ -42,6 +43,9 @@ while [[ $# -gt 0 ]] ; do
-xchacha)
OPT_XCHACHA="-xchacha"
;;
+ -aegis)
+ OPT_AEGIS="-aegis"
+ ;;
-*)
echo "Invalid option: $1"
usage
@@ -82,9 +86,10 @@ elif [[ $OPT_LOOPBACK -eq 1 ]]; then
"$HOME/go/src/github.com/hanwen/go-fuse/example/loopback/loopback" "$MNT" "$CRYPT" &
sleep 0.5
else
- echo -n "Testing gocryptfs $OPT_XCHACHA $OPT_OPENSSL at $CRYPT: "
+ echo -n "Testing gocryptfs $OPT_XCHACHA $OPT_AEGIS $OPT_OPENSSL at $CRYPT: "
gocryptfs -version
gocryptfs $OPT_XCHACHA -q -init -extpass="echo test" -scryptn=10 "$CRYPT"
+ gocryptfs $OPT_AEGIS -q -init -extpass="echo test" -scryptn=10 "$CRYPT"
gocryptfs $OPT_OPENSSL -q -extpass="echo test" "$CRYPT" "$MNT"
fi
diff --git a/build-without-aegis.bash b/build-without-aegis.bash
new file mode 100755
index 0000000..c20ca1a
--- /dev/null
+++ b/build-without-aegis.bash
@@ -0,0 +1,10 @@
+#!/bin/bash -eu
+
+cd "$(dirname "$0")"
+
+CGO_ENABLED=0 source ./build.bash -tags without_aegis
+
+if ldd gocryptfs 2> /dev/null ; then
+ echo "build-without-aegis.bash: error: compiled binary is not static"
+ exit 1
+fi
diff --git a/build-without-cgo.bash b/build-without-cgo.bash
new file mode 100755
index 0000000..753cc31
--- /dev/null
+++ b/build-without-cgo.bash
@@ -0,0 +1,10 @@
+#!/bin/bash -eu
+
+cd "$(dirname "$0")"
+
+CGO_ENABLED=0 source ./build.bash -tags without_openssl,without_aegis
+
+if ldd gocryptfs 2> /dev/null ; then
+ echo "build-without-cgo.bash: error: compiled binary is not static"
+ exit 1
+fi
diff --git a/build-without-openssl.bash b/build-without-openssl.bash
deleted file mode 100755
index d5dc218..0000000
--- a/build-without-openssl.bash
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash -eu
-
-cd "$(dirname "$0")"
-
-CGO_ENABLED=0 source ./build.bash -tags without_openssl
-
-if ldd gocryptfs 2> /dev/null ; then
- echo "build-without-openssl.bash: error: compiled binary is not static"
- exit 1
-fi
diff --git a/cli_args.go b/cli_args.go
index 2e9e796..26ebdd1 100644
--- a/cli_args.go
+++ b/cli_args.go
@@ -31,13 +31,13 @@ type argContainer struct {
longnames, allow_other, reverse, aessiv, nonempty, raw64,
noprealloc, speed, hkdf, serialize_reads, hh, info,
sharedstorage, fsck, one_file_system, deterministic_names,
- xchacha bool
+ xchacha, aegis bool
// Mount options with opposites
dev, nodev, suid, nosuid, exec, noexec, rw, ro, kernel_cache, acl bool
masterkey, mountpoint, cipherdir, cpuprofile,
memprofile, ko, ctlsock, fsname, force_owner, trace string
// FIDO2
- fido2 string
+ fido2 string
fido2_assert_options []string
// -extpass, -badname, -passfile can be passed multiple times
extpass, badname, passfile []string
@@ -188,6 +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")
// Mount options with opposites
flagSet.BoolVar(&args.dev, "dev", false, "Allow device files")
diff --git a/go.mod b/go.mod
index 3ebb832..325201f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,7 @@ module github.com/rfjakob/gocryptfs/v2
go 1.19
require (
+ github.com/aegis-aead/go-libaegis v0.2.10
github.com/aperturerobotics/jacobsa-crypto v1.0.2
github.com/hanwen/go-fuse/v2 v2.5.0
github.com/moby/sys/mountinfo v0.6.2
diff --git a/go.sum b/go.sum
index 3e4cb01..e92d87f 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+github.com/aegis-aead/go-libaegis v0.2.10 h1:5BJd8dgl2Fv3XwJ+W1dSWoS9FBaA61ryLnu2E4H2Vdw=
+github.com/aegis-aead/go-libaegis v0.2.10/go.mod h1:WKSG0pcdvpDrwDPBl9NdAIxHxpZOCqlKWH7EOnPLKHc=
github.com/aperturerobotics/jacobsa-crypto v1.0.2 h1:tNvVy1rev9FagnOyBmTcI6d23FfNceG9IujZROTRtlc=
github.com/aperturerobotics/jacobsa-crypto v1.0.2/go.mod h1:buWU1iY+FjIcfpb1aYfFJZfl07WlS7O30lTyC2iwjv8=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
diff --git a/gocryptfs-xray/xray_main.go b/gocryptfs-xray/xray_main.go
index 1c491f1..efba752 100644
--- a/gocryptfs-xray/xray_main.go
+++ b/gocryptfs-xray/xray_main.go
@@ -81,6 +81,7 @@ type argContainer struct {
encryptPaths *bool
aessiv *bool
xchacha *bool
+ aegis *bool
sep0 *bool
fido2 *string
version *bool
@@ -94,6 +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.fido2 = flag.String("fido2", "", "Protect the masterkey using a FIDO2 token instead of a password")
args.version = flag.Bool("version", false, "Print version information")
@@ -176,6 +178,8 @@ func inspectCiphertext(args *argContainer, fd *os.File) {
algo = cryptocore.BackendAESSIV
} else if *args.xchacha {
algo = cryptocore.BackendXChaCha20Poly1305
+ } else if *args.aegis {
+ algo = cryptocore.BackendAegis
}
headerBytes := make([]byte, contentenc.HeaderLen)
n, err := fd.ReadAt(headerBytes, 0)
diff --git a/init_dir.go b/init_dir.go
index d79a4b7..f4a871f 100644
--- a/init_dir.go
+++ b/init_dir.go
@@ -108,6 +108,7 @@ func initDir(args *argContainer) {
Fido2AssertOptions: args.fido2_assert_options,
DeterministicNames: args.deterministic_names,
XChaCha20Poly1305: args.xchacha,
+ Aegis: args.aegis,
LongNameMax: args.longnamemax,
Masterkey: handleArgsMasterkey(args),
})
diff --git a/internal/configfile/config_file.go b/internal/configfile/config_file.go
index 995a0c8..5e10228 100644
--- a/internal/configfile/config_file.go
+++ b/internal/configfile/config_file.go
@@ -32,7 +32,7 @@ type FIDO2Params struct {
// FIDO2 credential
CredentialID []byte
// FIDO2 hmac-secret salt
- HMACSalt []byte
+ HMACSalt []byte
AssertOptions []string
}
@@ -75,6 +75,7 @@ type CreateArgs struct {
Fido2AssertOptions []string
DeterministicNames bool
XChaCha20Poly1305 bool
+ Aegis bool
LongNameMax uint8
Masterkey []byte
}
@@ -92,6 +93,8 @@ func Create(args *CreateArgs) error {
cf.setFeatureFlag(FlagHKDF)
if args.XChaCha20Poly1305 {
cf.setFeatureFlag(FlagXChaCha20Poly1305)
+ } else if args.Aegis {
+ cf.setFeatureFlag(FlagAegis)
} 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.
@@ -119,9 +122,9 @@ func Create(args *CreateArgs) error {
if len(args.Fido2CredentialID) > 0 {
cf.setFeatureFlag(FlagFIDO2)
cf.FIDO2 = &FIDO2Params{
- CredentialID: args.Fido2CredentialID,
- HMACSalt: args.Fido2HmacSalt,
- AssertOptions: args.Fido2AssertOptions,
+ CredentialID: args.Fido2CredentialID,
+ HMACSalt: args.Fido2HmacSalt,
+ AssertOptions: args.Fido2AssertOptions,
}
}
// Catch bugs and invalid cli flag combinations early
@@ -133,7 +136,7 @@ func Create(args *CreateArgs) error {
key := args.Masterkey
if key == nil {
// Generate new random master key
- key = cryptocore.RandBytes(cryptocore.KeyLen)
+ key = cryptocore.RandBytes(cryptocore.MaxKeyLen)
}
tlog.PrintMasterkeyReminder(key)
// Encrypt it using the password
@@ -327,6 +330,9 @@ func (cf *ConfFile) ContentEncryption() (algo cryptocore.AEADTypeEnum, err error
if cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) {
return cryptocore.BackendXChaCha20Poly1305, nil
}
+ if cf.IsFeatureFlagSet(FlagAegis) {
+ return cryptocore.BackendAegis, nil
+ }
if cf.IsFeatureFlagSet(FlagAESSIV) {
return cryptocore.BackendAESSIV, nil
}
diff --git a/internal/configfile/feature_flags.go b/internal/configfile/feature_flags.go
index d6627a5..2722831 100644
--- a/internal/configfile/feature_flags.go
+++ b/internal/configfile/feature_flags.go
@@ -34,6 +34,8 @@ const (
FlagFIDO2
// FlagXChaCha20Poly1305 means we use XChaCha20-Poly1305 file content encryption
FlagXChaCha20Poly1305
+ // FlagAegis means we use Aegis file content encryption
+ FlagAegis
)
// knownFlags stores the known feature flags and their string representation
@@ -49,6 +51,7 @@ var knownFlags = map[flagIota]string{
FlagHKDF: "HKDF",
FlagFIDO2: "FIDO2",
FlagXChaCha20Poly1305: "XChaCha20Poly1305",
+ FlagAegis: "AEGIS",
}
// isFeatureFlagKnown verifies that we understand a feature flag.
diff --git a/internal/configfile/scrypt.go b/internal/configfile/scrypt.go
index 0ce8777..b82a431 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.KeyLen)
+ s.Salt = cryptocore.RandBytes(cryptocore.MaxKeyLen)
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.KeyLen
+ s.KeyLen = cryptocore.MaxKeyLen
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.KeyLen {
- return fmt.Errorf("Fatal: scrypt parameter KeyLen below minimum: value=%d, min=%d", s.KeyLen, cryptocore.KeyLen)
+ if s.KeyLen < cryptocore.MinKeyLen {
+ return fmt.Errorf("Fatal: scrypt parameter KeyLen below minimum: value=%d, min=%d", s.KeyLen, cryptocore.MinKeyLen)
}
return nil
}
diff --git a/internal/configfile/validate.go b/internal/configfile/validate.go
index ab8917d..333eea6 100644
--- a/internal/configfile/validate.go
+++ b/internal/configfile/validate.go
@@ -38,8 +38,13 @@ func (cf *ConfFile) Validate() error {
return fmt.Errorf("XChaCha20Poly1305 requires HKDF feature flag")
}
}
+ if cf.IsFeatureFlagSet(FlagAegis) {
+ if cf.IsFeatureFlagSet(FlagGCMIV128) {
+ return fmt.Errorf("AEGIS conflicts with GCMIV128 feature flag")
+ }
+ }
// The absence of other flags means AES-GCM (oldest algorithm)
- if !cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) && !cf.IsFeatureFlagSet(FlagAESSIV) {
+ if !cf.IsFeatureFlagSet(FlagAegis) && !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 b20ccb1..2a34307 100644
--- a/internal/contentenc/content_test.go
+++ b/internal/contentenc/content_test.go
@@ -22,7 +22,8 @@ func TestSplitRange(t *testing.T) {
{6654, 8945},
}
- key := make([]byte, cryptocore.KeyLen)
+ keyLen := cryptocore.BackendGoGCM.KeyLen
+ key := make([]byte, keyLen)
cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true)
f := New(cc, DefaultBS)
@@ -50,7 +51,8 @@ func TestCiphertextRange(t *testing.T) {
{6654, 8945},
}
- key := make([]byte, cryptocore.KeyLen)
+ keyLen := cryptocore.BackendGoGCM.KeyLen
+ key := make([]byte, keyLen)
cc := cryptocore.New(key, cryptocore.BackendGoGCM, DefaultIVBits, true)
f := New(cc, DefaultBS)
@@ -73,7 +75,8 @@ func TestCiphertextRange(t *testing.T) {
}
func TestBlockNo(t *testing.T) {
- key := make([]byte, cryptocore.KeyLen)
+ keyLen := cryptocore.BackendGoGCM.KeyLen
+ key := make([]byte, 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 b35964a..0118c5e 100644
--- a/internal/contentenc/offsets_test.go
+++ b/internal/contentenc/offsets_test.go
@@ -9,7 +9,8 @@ import (
// TestSizeToSize tests CipherSizeToPlainSize and PlainSizeToCipherSize
func TestSizeToSize(t *testing.T) {
- key := make([]byte, cryptocore.KeyLen)
+ keyLen := cryptocore.BackendGoGCM.KeyLen
+ key := make([]byte, 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 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)
}
diff --git a/internal/exitcodes/exitcodes.go b/internal/exitcodes/exitcodes.go
index 508ba38..6bf7672 100644
--- a/internal/exitcodes/exitcodes.go
+++ b/internal/exitcodes/exitcodes.go
@@ -72,6 +72,8 @@ const (
DevNull = 30
// FIDO2Error - an error was encountered while interacting with a FIDO2 token
FIDO2Error = 31
+ // Aegis means you tried to enable Aegis, but we were compiled without it.
+ Aegis = 32
)
// Err wraps an error with an associated numeric exit code
diff --git a/internal/fusefrontend/xattr_unit_test.go b/internal/fusefrontend/xattr_unit_test.go
index 86c87a7..5f69e00 100644
--- a/internal/fusefrontend/xattr_unit_test.go
+++ b/internal/fusefrontend/xattr_unit_test.go
@@ -16,7 +16,8 @@ import (
func newTestFS(args Args) *RootNode {
// Init crypto backend
- key := make([]byte, cryptocore.KeyLen)
+ keyLen := cryptocore.BackendGoGCM.KeyLen
+ key := make([]byte, 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 7a4e915..be18f92 100644
--- a/internal/nametransform/longnames_test.go
+++ b/internal/nametransform/longnames_test.go
@@ -34,7 +34,8 @@ func TestRemoveLongNameSuffix(t *testing.T) {
}
func newLognamesTestInstance(longNameMax uint8) *NameTransform {
- key := make([]byte, cryptocore.KeyLen)
+ keyLen := cryptocore.BackendGoGCM.KeyLen
+ key := make([]byte, 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 d6fa12e..e60e37d 100644
--- a/internal/speed/speed.go
+++ b/internal/speed/speed.go
@@ -47,13 +47,14 @@ func Run() {
{name: cryptocore.BackendAESSIV.String(), f: bAESSIV, preferred: false},
{name: cryptocore.BackendXChaCha20Poly1305OpenSSL.String(), f: bStupidXchacha, preferred: stupidgcm.PreferOpenSSLXchacha20poly1305()},
{name: cryptocore.BackendXChaCha20Poly1305.String(), f: bXchacha20poly1305, preferred: !stupidgcm.PreferOpenSSLXchacha20poly1305()},
+ {name: cryptocore.BackendAegis.String(), f: bAegis, preferred: false},
}
testing.Init()
for _, b := range bTable {
fmt.Printf("%-26s\t", b.name)
mbs := mbPerSec(testing.Benchmark(b.f))
if mbs > 0 {
- fmt.Printf("%7.2f MB/s", mbs)
+ fmt.Printf("%8.2f MB/s", mbs)
} else {
fmt.Printf(" N/A")
}
@@ -168,3 +169,9 @@ func bStupidXchacha(b *testing.B) {
}
bEncrypt(b, stupidgcm.NewXchacha20poly1305(randBytes(32)))
}
+
+// bAegis benchmarks Aegis from github.com/aegis-aead/go-libaegis
+func bAegis(b *testing.B) {
+ c := stupidgcm.NewAegis(randBytes(16))
+ bEncrypt(b, c)
+}
diff --git a/internal/stupidgcm/Makefile b/internal/stupidgcm/Makefile
index 143819d..d8f7d2e 100644
--- a/internal/stupidgcm/Makefile
+++ b/internal/stupidgcm/Makefile
@@ -3,11 +3,15 @@ test: gcc
# All three ways of building this must work
go build
go build -tags without_openssl
- CGO_ENABLED=0 go build -tags without_openssl
+ go build -tags without_aegis
+ go build -tags without_openssl,without_aegis
+ CGO_ENABLED=0 go build -tags without_openssl,without_aegis
# Likewise, all three ways of testing this must work
go test -v
go test -v -tags without_openssl
- CGO_ENABLED=0 go test -v -tags without_openssl
+ go test -v -tags without_aegis
+ go test -v -tags without_openssl,without_aegis
+ CGO_ENABLED=0 go test -v -tags without_openssl,without_aegis
.PHONY: gcc
gcc:
diff --git a/internal/stupidgcm/aegis.go b/internal/stupidgcm/aegis.go
new file mode 100644
index 0000000..8975055
--- /dev/null
+++ b/internal/stupidgcm/aegis.go
@@ -0,0 +1,57 @@
+//go:build !without_aegis && cgo
+// +build !without_aegis,cgo
+
+package stupidgcm
+
+import (
+ "crypto/cipher"
+ "log"
+
+ "github.com/aegis-aead/go-libaegis/aegis128x2"
+ "github.com/aegis-aead/go-libaegis/common"
+)
+
+const (
+ // BuiltWithoutAegis indicates if aegis been disabled at compile-time
+ BuiltWithoutAegis = !common.Available
+
+ // Aegis supports 16 and 32 bit tags
+ AegisTagLen = 16
+)
+
+type stupidAegis struct {
+ aead cipher.AEAD
+}
+
+// Verify that we satisfy the cipher.AEAD interface
+var _ cipher.AEAD = &stupidAegis{}
+
+func (*stupidAegis) NonceSize() int {
+ return aegis128x2.NonceSize
+}
+
+func (*stupidAegis) Overhead() int {
+ return AegisTagLen
+}
+
+func NewAegis(key []byte) cipher.AEAD {
+ aead, err := aegis128x2.New(key, AegisTagLen)
+ if err != nil {
+ log.Panic(err)
+ }
+ return &stupidAegis{
+ aead: aead,
+ }
+}
+
+func (x *stupidAegis) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
+ return x.aead.Seal(dst, nonce, plaintext, additionalData)
+}
+
+func (x *stupidAegis) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
+ return x.aead.Open(dst, nonce, ciphertext, additionalData)
+}
+
+func (x *stupidAegis) Wipe() {
+ x.aead.(*aegis128x2.Aegis128X2).Wipe()
+}
diff --git a/internal/stupidgcm/aegis_test.go b/internal/stupidgcm/aegis_test.go
new file mode 100644
index 0000000..36ef763
--- /dev/null
+++ b/internal/stupidgcm/aegis_test.go
@@ -0,0 +1,16 @@
+//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/internal/stupidgcm/chacha.go b/internal/stupidgcm/chacha.go
index de0c2e8..dcdcb3a 100644
--- a/internal/stupidgcm/chacha.go
+++ b/internal/stupidgcm/chacha.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
package stupidgcm
diff --git a/internal/stupidgcm/chacha_test.go b/internal/stupidgcm/chacha_test.go
index 542ff15..4fa038b 100644
--- a/internal/stupidgcm/chacha_test.go
+++ b/internal/stupidgcm/chacha_test.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
package stupidgcm
diff --git a/internal/stupidgcm/common.go b/internal/stupidgcm/common.go
index d88dc62..c5dd338 100644
--- a/internal/stupidgcm/common.go
+++ b/internal/stupidgcm/common.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
package stupidgcm
diff --git a/internal/stupidgcm/common_test.go b/internal/stupidgcm/common_test.go
index 7f38e90..47fee7c 100644
--- a/internal/stupidgcm/common_test.go
+++ b/internal/stupidgcm/common_test.go
@@ -246,6 +246,8 @@ func testWipe(t *testing.T, c cipher.AEAD) {
t.Fatal("c2.key is not zeroed")
}
}
+ case *stupidAegis:
+ c2.Wipe() // AEGIS has its own Wipe method
default:
t.Fatalf("BUG: unhandled type %T", c2)
}
diff --git a/internal/stupidgcm/doc.go b/internal/stupidgcm/doc.go
index dce82ae..f5d4177 100644
--- a/internal/stupidgcm/doc.go
+++ b/internal/stupidgcm/doc.go
@@ -1,5 +1,5 @@
-// Package stupidgcm wraps OpenSSL to provide a cipher.AEAD interface for
-// authenticated encryption algorithms.
+// Package stupidgcm wraps OpenSSL and libaegis to provide a cipher.AEAD
+// interface for authenticated encryption algorithms.
//
// The supported algorithms are:
//
@@ -9,6 +9,8 @@
//
// (3) XChaCha20-Poly1305 (OpenSSL EVP_chacha20_poly1305 + Go HChaCha20)
//
+// (4) AEGIS (go-libaegis)
+//
// The golang.org/x/crypto libraries provides implementations for all algorithms,
// and the test suite verifies that the implementation in this package gives
// the exact same results.
diff --git a/internal/stupidgcm/gcm.go b/internal/stupidgcm/gcm.go
index 2e5aac4..738021e 100644
--- a/internal/stupidgcm/gcm.go
+++ b/internal/stupidgcm/gcm.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
package stupidgcm
diff --git a/internal/stupidgcm/gcm_test.go b/internal/stupidgcm/gcm_test.go
index c730a87..6c15287 100644
--- a/internal/stupidgcm/gcm_test.go
+++ b/internal/stupidgcm/gcm_test.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
// We compare against Go's built-in GCM implementation. Since stupidgcm only
// supports 128-bit IVs and Go only supports that from 1.5 onward, we cannot
diff --git a/internal/stupidgcm/locking.go b/internal/stupidgcm/locking.go
index 04cf232..29f8332 100644
--- a/internal/stupidgcm/locking.go
+++ b/internal/stupidgcm/locking.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
package stupidgcm
diff --git a/internal/stupidgcm/openssl.go b/internal/stupidgcm/openssl.go
index 8c950f8..0360629 100644
--- a/internal/stupidgcm/openssl.go
+++ b/internal/stupidgcm/openssl.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
package stupidgcm
diff --git a/internal/stupidgcm/without_aegis.go b/internal/stupidgcm/without_aegis.go
new file mode 100644
index 0000000..efd665c
--- /dev/null
+++ b/internal/stupidgcm/without_aegis.go
@@ -0,0 +1,28 @@
+//go:build without_aegis || !cgo
+// +build without_aegis !cgo
+
+package stupidgcm
+
+import (
+ "fmt"
+ "os"
+
+ "crypto/cipher"
+
+ "github.com/rfjakob/gocryptfs/v2/internal/exitcodes"
+)
+
+const (
+ // BuiltWithoutAegis indicates if openssl been disabled at compile-time
+ BuiltWithoutAegis = true
+)
+
+type stupidAegis struct {
+ aead cipher.AEAD
+}
+
+func NewAegis(_ []byte) cipher.AEAD {
+ fmt.Fprintln(os.Stderr, "I have been compiled without aegis support but you are still trying to use aegis")
+ os.Exit(exitcodes.Aegis)
+ return nil
+}
diff --git a/internal/stupidgcm/without_openssl.go b/internal/stupidgcm/without_openssl.go
index fcef793..1901c78 100644
--- a/internal/stupidgcm/without_openssl.go
+++ b/internal/stupidgcm/without_openssl.go
@@ -1,5 +1,5 @@
-//go:build without_openssl
-// +build without_openssl
+//go:build without_openssl || !cgo
+// +build without_openssl !cgo
package stupidgcm
diff --git a/internal/stupidgcm/xchacha.go b/internal/stupidgcm/xchacha.go
index 3c121ba..0187da8 100644
--- a/internal/stupidgcm/xchacha.go
+++ b/internal/stupidgcm/xchacha.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
diff --git a/internal/stupidgcm/xchacha_test.go b/internal/stupidgcm/xchacha_test.go
index 676a023..bd62c04 100644
--- a/internal/stupidgcm/xchacha_test.go
+++ b/internal/stupidgcm/xchacha_test.go
@@ -1,5 +1,5 @@
-//go:build !without_openssl
-// +build !without_openssl
+//go:build !without_openssl && cgo
+// +build !without_openssl,cgo
package stupidgcm
diff --git a/masterkey.go b/masterkey.go
index d488441..90f0adf 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.KeyLen {
- tlog.Fatal.Printf("Master key has length %d but we require length %d", len(key), cryptocore.KeyLen)
+ if len(key) != cryptocore.MaxKeyLen {
+ tlog.Fatal.Printf("Master key has length %d but we require length %d", len(key), cryptocore.MaxKeyLen)
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.KeyLen)
+ return make([]byte, cryptocore.MaxKeyLen)
}
// No master key source specified on the command line. Caller must parse
// the config file.
diff --git a/mount.go b/mount.go
index 0eaa3dd..c9fa639 100644
--- a/mount.go
+++ b/mount.go
@@ -265,6 +265,9 @@ func initFuseFrontend(args *argContainer) (rootNode fs.InodeEmbedder, wipeKeys f
}
IVBits = chacha20poly1305.NonceSizeX * 8
}
+ if args.aegis {
+ cryptoBackend = cryptocore.BackendAegis
+ }
// forceOwner implies allow_other, as documented.
// Set this early, so args.allow_other can be relied on below this point.
if args._forceOwner != nil {
diff --git a/package-release-tarballs.bash b/package-release-tarballs.bash
index fde214e..66c8370 100755
--- a/package-release-tarballs.bash
+++ b/package-release-tarballs.bash
@@ -49,7 +49,7 @@ package_source() {
package_static_binary() {
# Compiles the gocryptfs binary and sets $GITVERSION
- source build-without-openssl.bash
+ source build-without-cgo.bash
if ldd gocryptfs > /dev/null ; then
echo "error: compiled gocryptfs binary is not static"
diff --git a/test.bash b/test.bash
index 174236e..70a6a36 100755
--- a/test.bash
+++ b/test.bash
@@ -51,8 +51,8 @@ fi
# Clean up dangling filesystems and don't exit if we found some
unmount_leftovers || true
-./build-without-openssl.bash || {
- echo "$MYNAME: build-without-openssl.bash failed"
+./build-without-cgo.bash || {
+ echo "$MYNAME: build-without-cgo.bash failed"
exit 1
}
# Don't build with openssl if we were passed "-tags without_openssl"
diff --git a/tests/matrix/matrix_test.go b/tests/matrix/matrix_test.go
index 417e126..625a423 100644
--- a/tests/matrix/matrix_test.go
+++ b/tests/matrix/matrix_test.go
@@ -72,8 +72,17 @@ var matrix = []testcaseMatrix{
{false, "false", false, true, []string{"-xchacha"}},
}
+var matrixAegisAdditions = []testcaseMatrix{
+ {false, "auto", false, false, []string{"-aegis"}},
+ {true, "auto", false, false, []string{"-aegis"}},
+}
+
// This is the entry point for the tests
func TestMain(m *testing.M) {
+ if !stupidgcm.BuiltWithoutAegis {
+ matrix = append(matrix, matrixAegisAdditions...)
+ }
+
// Make "testing.Verbose()" return the correct value
flag.Parse()
var i int