Compare commits

...

79 Commits

Author SHA1 Message Date
Matéo Duparc f0e45c7b7e
libgocryptfs: update to gocryptfs v2.1 2021-08-29 12:46:32 +02:00
Jakob Unterwurzacher 91d3b30c1c doc: file-format.md: describe XChaCha20-Poly1305
Different nonce size.
2021-08-26 08:43:41 +02:00
Jakob Unterwurzacher 7b25ff39c8 fsstress-gocryptfs: fuse-xfstests now lives in /opt
fuse-xfstests should be installed to /opt now to make
the terminal output independent of the user name
(as done in https://github.com/rfjakob/fuse-xfstests/wiki/results_2021-06-02 )
2021-08-26 07:46:28 +02:00
Jakob Unterwurzacher 61ef6b00a6 -devrandom: make flag a no-op
Commit f3c777d5ea added the `-devrandom` option:

    commit f3c777d5ea
    Author: @slackner
    Date:   Sun Nov 19 13:30:04 2017 +0100

    main: Add '-devrandom' commandline option

    Allows to use /dev/random for generating the master key instead of the
    default Go implementation. When the kernel random generator has been
    properly initialized both are considered equally secure, however:

    * Versions of Go prior to 1.9 just fall back to /dev/urandom if the
      getrandom() syscall would be blocking (Go Bug #19274)

    * Kernel versions prior to 3.17 do not support getrandom(), and there
      is no check if the random generator has been properly initialized
      before reading from /dev/urandom

    This is especially useful for embedded hardware with low-entroy. Please
    note that generation of the master key might block indefinitely if the
    kernel cannot harvest enough entropy.

We now require Go v1.13 and Kernel versions should have also moved on.
Make the flag a no-op.

https://github.com/rfjakob/gocryptfs/issues/596
2021-08-25 12:39:17 +02:00
Jakob Unterwurzacher b3d26b7264 go mod: update go-fuse
We want /dev/fd/N support:

  74a933d6e8
  "fuse: support special /dev/fd/N mountpoint"

Fixes https://github.com/rfjakob/gocryptfs/issues/590
2021-08-25 12:17:26 +02:00
Jakob Unterwurzacher a4ed1aab00 README: add -xchacha to changelog
https://github.com/rfjakob/gocryptfs/issues/452
2021-08-24 14:06:01 +02:00
Jakob Unterwurzacher 24bb28a517 MANPAGE: add -xchacha 2021-08-24 14:05:52 +02:00
Jakob Unterwurzacher 5f1094b164 -speed: note that -xchacha is selectable 2021-08-24 14:02:12 +02:00
Jakob Unterwurzacher ab7308639b tests/example_filesystems: add v2.2-xchacha-deterministic-names
Combines both new flags.
2021-08-24 13:53:50 +02:00
Jakob Unterwurzacher d70875aaa9 tests/example_filesystems: add deterministic-names and xchacha 2021-08-24 13:50:07 +02:00
Jakob Unterwurzacher 62ed081c5e benchmark.bash: add -xchacha support 2021-08-24 13:30:09 +02:00
Jakob Unterwurzacher abaa12992c xray: add xchacha support
Also use the new cryptocore algo names.
2021-08-23 22:19:30 +02:00
Jakob Unterwurzacher 20ca63cdbc contentenc: remove unused NonceMode constants
Looks like these are part of an abandoned plan.
2021-08-23 22:14:20 +02:00
Jakob Unterwurzacher dfb7fae52a speed: use algo names from cryptocore 2021-08-23 22:13:49 +02:00
Jakob Unterwurzacher 806334eacf cryptocore: add NonceSize to AEADTypeEnum
Have the information in one centralized place,
and access it from main as needed.
2021-08-23 22:10:23 +02:00
Jakob Unterwurzacher b12ad292d4 tests/cli: add -xchacha tests 2021-08-23 16:17:04 +02:00
Jakob Unterwurzacher 2fb1d52746 tests/matrix: add -xchacha test 2021-08-23 16:00:41 +02:00
Jakob Unterwurzacher 97d8340bd8 configfile: add Validate() function, support FlagXChaCha20Poly1305
We used to do validation using lists of mandatory feature flags.

With the introduction of XChaCha20Poly1305, this became too
simplistic, as it uses a different IV length, hence disabling
GCMIV128.

Add a dedicated function, Validate(), with open-coded validation
logic.

The validation and creation logic also gets XChaCha20Poly1305
support, and gocryptfs -init -xchacha now writes the flag into
gocryptfs.conf.
2021-08-23 16:00:41 +02:00
Jakob Unterwurzacher 4764a9bde0 Add partial XChaCha20-Poly1305 support (mount flag only)
Mount flag only at the moment, not saved to gocryptfs.conf.

https://github.com/rfjakob/gocryptfs/issues/452
2021-08-23 16:00:41 +02:00
Jakob Unterwurzacher b02812f8b3 test/cli: actually run TestZerokey
As the filename did not end in _test.go, TestZerokey
was not actually run. Fix that.
	renamed:    tests/cli/zerokey.go -> tests/cli/zerokey_test.go
2021-08-23 15:17:04 +02:00
Jakob Unterwurzacher 69d88505fd go mod: declare module version v2
Our git version is v2+ for some time now, but go.mod
still declared v1. Hopefully making both match makes
https://pkg.go.dev/github.com/rfjakob/gocryptfs/v2 work.

All the import paths have been fixed like this:

  find . -name \*.go | xargs sed -i s%github.com/rfjakob/gocryptfs/%github.com/rfjakob/gocryptfs/v2/%
2021-08-23 15:05:15 +02:00
Jakob Unterwurzacher c9abfc8f06 ensurefds012: package comment should preceded package statement
This makes the comment visible to godoc.
2021-08-23 11:04:22 +02:00
Jakob Unterwurzacher b603169d2c configfile: pass struct to Create 2/2
Drop Create and rename Create2 to Create.
2021-08-21 14:04:04 +02:00
Jakob Unterwurzacher 4b93525249 configfile: pass struct to Create 1/2
The argument list got too long.

Part 1: Replace with Create2
2021-08-21 14:01:58 +02:00
Jakob Unterwurzacher 2da0e13b1d cryptocore: drop IVLen helper var
The IVLen var seems be a net loss in clarity. Drop it.

Also add comments and normalize error messages.
2021-08-21 10:55:20 +02:00
Jakob Unterwurzacher f6be765ef6 README: Update changelog with -deterministic-names
Fixes https://github.com/rfjakob/gocryptfs/issues/151
Fixes https://github.com/rfjakob/gocryptfs/issues/402
Fixes https://github.com/rfjakob/gocryptfs/pull/592

Partial-fix https://github.com/rfjakob/gocryptfs/issues/108
2021-08-20 17:16:23 +02:00
Jakob Unterwurzacher fbccb16043 -deterministic-names: implement for reverse mode, too 2021-08-20 17:06:18 +02:00
Jakob Unterwurzacher 14bf80301b MANPAGE: move nosyslog to MOUNT OPTIONS section
It was in INIT OPTIONS by mistake.
2021-08-20 16:01:53 +02:00
Jakob Unterwurzacher 2a9dea2973 -deterministic-names: accept flag on -init
And store it in gocryptfs.conf (=remove DirIV feature flag).
2021-08-20 15:57:40 +02:00
Jakob Unterwurzacher 195d9d18a9 Implement -deterministic-names: extended -zerodiriv
-deterministc-names uses all-zero dirivs but does not write
them to disk anymore.
2021-08-20 10:58:42 +02:00
Jose M Perez 8f94083a21 Flag -zerodiriv to create all diriv as all zero byte files 2021-08-19 18:05:54 +02:00
Jakob Unterwurzacher 02c91d73ce syscallcompat: use early return in asUser() 2021-08-19 09:01:58 +02:00
Jakob Unterwurzacher be2bd4eec7 golangci-lint: fix issues found by "unused" and "deadcode"
Except xattrSupported, this is a false positive.

$ golangci-lint run --disable-all --enable unused --enable deadcode
gocryptfs-xray/xray_main.go:24:5: `GitVersionFuse` is unused (deadcode)
var GitVersionFuse = "[GitVersionFuse not set - please compile using ./build.bash]"
    ^
tests/symlink_race/main.go:47:6: `chmodLoop` is unused (deadcode)
func chmodLoop() {
     ^
internal/readpassword/extpass_test.go:11:5: `testPw` is unused (deadcode)
var testPw = []byte("test")
    ^
tests/reverse/xattr_test.go:13:6: func `xattrSupported` is unused (unused)
func xattrSupported(path string) bool {
     ^
internal/fusefrontend_reverse/rpath.go:20:22: func `(*RootNode).abs` is unused (unused)
func (rfs *RootNode) abs(relPath string, err error) (string, error) {
                     ^
tests/matrix/matrix_test.go:310:6: `sContains` is unused (deadcode)
func sContains(haystack []string, needle string) bool {
2021-08-19 08:34:49 +02:00
Jakob Unterwurzacher d8b8232c3c test_helpers: actually use global testParentDir variable
Typo inside doInit.
2021-08-19 08:34:49 +02:00
Jakob Unterwurzacher f3d927e590 fsck: sort files alphabetically again
This makes fsck runs deterministic again.

Sorting (commit quoted below) got lost while
moving to go-fuse v2 api.

commit e6caf56ea4
Author: Jakob Unterwurzacher <jakobunt@gmail.com>
Date:   Mon Apr 2 16:56:29 2018 +0200

    fsck: sort files alphabetically

    This makes fsck runs deterministic.
2021-08-19 08:34:49 +02:00
Jakob Unterwurzacher 8ee595dd48 Fix issues found by "go vet"
Issues were:

 # github.com/rfjakob/gocryptfs/contrib/findholes/holes
 contrib/findholes/holes/holes.go:136:2: unreachable code
 # github.com/rfjakob/gocryptfs/tests/root_test_test
 tests/root_test/root_test.go:139:2: unreachable code

Also make sure we actually run "go vet" against the whole
codebase.
2021-08-19 08:34:49 +02:00
Jakob Unterwurzacher c86981342b golangci-lint: fix issues found by gosimple
Everything except the

	if err2.Err == syscall.EOPNOTSUPP

case. Gets too confusing when collapsed into a single line.

Issues were:

$ golangci-lint run --disable-all --enable gosimple
mount.go:473:2: S1008: should use 'return strings.HasPrefix(v, "fusermount version")' instead of 'if strings.HasPrefix(v, "fusermount version") { return true }; return false' (gosimple)
	if strings.HasPrefix(v, "fusermount version") {
	^
cli_args.go:258:5: S1002: should omit comparison to bool constant, can be simplified to `args.forcedecode` (gosimple)
	if args.forcedecode == true {
	   ^
cli_args.go:263:6: S1002: should omit comparison to bool constant, can be simplified to `args.aessiv` (gosimple)
		if args.aessiv == true {
		   ^
cli_args.go:267:6: S1002: should omit comparison to bool constant, can be simplified to `args.reverse` (gosimple)
		if args.reverse == true {
		   ^
internal/stupidgcm/stupidgcm.go:227:6: S1002: should omit comparison to bool constant, can be simplified to `g.forceDecode` (gosimple)
		if g.forceDecode == true {
		   ^
gocryptfs-xray/xray_tests/xray_test.go:23:5: S1004: should use !bytes.Equal(out, expected) instead (gosimple)
	if bytes.Compare(out, expected) != 0 {
	   ^
gocryptfs-xray/xray_tests/xray_test.go:40:5: S1004: should use !bytes.Equal(out, expected) instead (gosimple)
	if bytes.Compare(out, expected) != 0 {
	   ^
gocryptfs-xray/paths_ctlsock.go:34:20: S1002: should omit comparison to bool constant, can be simplified to `!eof` (gosimple)
	for eof := false; eof == false; line++ {
	                  ^
tests/reverse/xattr_test.go:19:2: S1008: should use 'return err2.Err != syscall.EOPNOTSUPP' instead of 'if err2.Err == syscall.EOPNOTSUPP { return false }; return true' (gosimple)
	if err2.Err == syscall.EOPNOTSUPP {
	^
internal/fusefrontend/node.go:459:45: S1002: should omit comparison to bool constant, can be simplified to `!nameFileAlreadyThere` (gosimple)
		if nametransform.IsLongContent(cName2) && nameFileAlreadyThere == false {
		                                          ^
tests/xattr/xattr_integration_test.go:221:2: S1008: should use 'return err2.Err != syscall.EOPNOTSUPP' instead of 'if err2.Err == syscall.EOPNOTSUPP { return false }; return true' (gosimple)
	if err2.Err == syscall.EOPNOTSUPP {
	^
tests/test_helpers/helpers.go:338:19: S1002: should omit comparison to bool constant, can be simplified to `open` (gosimple)
	if err != nil && open == true {
	                 ^
tests/matrix/concurrency_test.go:121:7: S1004: should use !bytes.Equal(buf, content) instead (gosimple)
			if bytes.Compare(buf, content) != 0 {
			   ^
2021-08-19 08:34:44 +02:00
Jakob Unterwurzacher 2a25c3a8fd tlog: switch from golang.org/x/crypto/ssh/terminal to golang.org/x/term
$ golangci-lint run

internal/tlog/log.go:13:2: SA1019: package golang.org/x/crypto/ssh/terminal is deprecated: this package moved to golang.org/x/term. (staticcheck)
	"golang.org/x/crypto/ssh/terminal"
2021-08-19 07:38:56 +02:00
Jakob Unterwurzacher 9c268fbe88 README: update for v2.1 release 2021-08-18 17:48:38 +02:00
Jakob Unterwurzacher b2abb0484f Drop workarounds for Go 1.11 and Go 1.12 2021-08-18 17:48:38 +02:00
Jakob Unterwurzacher a5f88e86d1 github ci: drop Go 1.11
Contemporary x/sys/unix does not compile anymore with Go 1.11:

https://github.com/rfjakob/gocryptfs/runs/3362218517?check_suite_focus=true

+ GOOS=darwin
+ GOARCH=amd64
+ go build -tags without_openssl -o /dev/null
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/fcntl_darwin.go:11:9: undefined: fcntl
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/fcntl_darwin.go:16:12: undefined: fcntl
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/fcntl_darwin.go:22:12: undefined: fcntl
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/ioctl.go:21:9: undefined: ioctl
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/ioctl.go:30:9: undefined: ioctl
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/ioctl.go:39:9: undefined: ioctl
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/ioctl.go:49:9: undefined: ioctl
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/ioctl.go:61:9: undefined: ioctl
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/syscall_bsd.go:646:10: undefined: mmap
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/syscall_bsd.go:647:10: undefined: munmap
Error: ../../../go/pkg/mod/golang.org/x/sys@v0.0.0-20210817190340-bfb29a6856f2/unix/ioctl.go:61:9: too many errors
note: module requires Go 1.17make: *** [Makefile:44: ci] Error 2
Error: Process completed with exit code 2.
2021-08-18 17:30:01 +02:00
Jakob Unterwurzacher a7fa91764a Update dependencies
Using

  go get -u
  go mod tidy
2021-08-18 17:30:01 +02:00
Jakob Unterwurzacher bc72e58fac README: update changelog 2021-08-18 16:08:14 +02:00
Jakob Unterwurzacher 64793fedf4 Fix issues found by ineffassign
gocryptfs$ ineffassign ./...

/home/jakob/go/src/github.com/rfjakob/gocryptfs/internal/configfile/config_file.go:243:2: ineffectual assignment to scryptHash
/home/jakob/go/src/github.com/rfjakob/gocryptfs/internal/configfile/config_file.go:272:2: ineffectual assignment to scryptHash
/home/jakob/go/src/github.com/rfjakob/gocryptfs/internal/fusefrontend/file.go:285:3: ineffectual assignment to fileID
/home/jakob/go/src/github.com/rfjakob/gocryptfs/internal/fusefrontend/node.go:367:3: ineffectual assignment to err
/home/jakob/go/src/github.com/rfjakob/gocryptfs/internal/fusefrontend/node_open_create.go:68:2: ineffectual assignment to fd
/home/jakob/go/src/github.com/rfjakob/gocryptfs/mount.go:308:2: ineffectual assignment to masterkey
/home/jakob/go/src/github.com/rfjakob/gocryptfs/gocryptfs-xray/xray_main.go:156:13: ineffectual assignment to err
/home/jakob/go/src/github.com/rfjakob/gocryptfs/internal/fusefrontend/prepare_syscall_test.go:65:16: ineffectual assignment to errno
/home/jakob/go/src/github.com/rfjakob/gocryptfs/internal/syscallcompat/open_nofollow_test.go:34:2: ineffectual assignment to fd
/home/jakob/go/src/github.com/rfjakob/gocryptfs/tests/defaults/acl_test.go:111:6: ineffectual assignment to err
/home/jakob/go/src/github.com/rfjakob/gocryptfs/tests/defaults/acl_test.go:181:2: ineffectual assignment to sz
/home/jakob/go/src/github.com/rfjakob/gocryptfs/tests/defaults/acl_test.go:198:2: ineffectual assignment to sz
/home/jakob/go/src/github.com/rfjakob/gocryptfs/tests/defaults/main_test.go:365:8: ineffectual assignment to err
/home/jakob/go/src/github.com/rfjakob/gocryptfs/tests/xattr/xattr_fd_test.go:30:6: ineffectual assignment to err
/home/jakob/go/src/github.com/rfjakob/gocryptfs/tests/xattr/xattr_fd_test.go:66:6: ineffectual assignment to err
2021-08-18 15:48:01 +02:00
Jakob Unterwurzacher dc52e32151 MANPAGE: add "exclude all but" example
Fixes https://github.com/rfjakob/gocryptfs/issues/588
2021-08-18 11:39:01 +02:00
Jakob Unterwurzacher 0bc9784508 reverse: fix "exclude all but" case
With test.

Fixes https://github.com/rfjakob/gocryptfs/issues/588
2021-08-18 11:38:56 +02:00
Jakob Unterwurzacher 3df1c62430 tests/reverse/TestExcludeTestFs: test trailing slash 2021-08-18 11:04:56 +02:00
Jakob Unterwurzacher 884398eec3 tests/reverse/TestExcludeTestFs: improve comments & code style
No functional changes.
2021-08-18 11:04:40 +02:00
Jakob Unterwurzacher 022c169c39 MANPAGE: -ew: make gitignore syntax more prominent
https://github.com/rfjakob/gocryptfs/issues/588
2021-08-18 10:37:53 +02:00
Jakob Unterwurzacher b8ddc49ede tests/cli/TestBadname: make sure case 5 is never decodable
Sometimes, by chance, case 5 resulted in valid decrypted data:

--- FAIL: TestBadname (0.08s)
    cli_test.go:885: Case 5 failed: "J7Rbo1BvfXojpBEr0Qrt_invalid_file GOCRYPTFS_BAD_NAME" in ["file GOCRYPTFS_BAD_NAME,\x9e$O\xc3j\x8c\xd0\x06\x01#\f%k\x02\xcanvalid_file GOCRYPTFS_BAD_NAME,mzaZRF9_0IU-_5vv2wPC_invalid_file GOCRYPTFS_BAD_NAME,file,file_invalid_file GOCRYPTFS_BAD_NAME,mzaZRF9_0IU-_5vv2wP_invalid_file GOCRYPTFS_BAD_NAME"]

Add percent signs so base64 decoding always fails.

Fixes https://github.com/rfjakob/gocryptfs/runs/3347883728
2021-08-17 15:17:36 +02:00
Jakob Unterwurzacher 8d5b4c5177 github actions ci: test different Go versions 2021-08-17 09:08:26 +02:00
Jakob Unterwurzacher eeb267950a tests/reverse: replace os.ReadDir to support older Go versions 2021-08-16 22:05:33 +02:00
Jakob Unterwurzacher e2d3834b1c tests/root_test: show failing command detail
https://github.com/kdave/btrfs-progs/issues/392
2021-08-16 19:24:04 +02:00
Jakob Unterwurzacher b2724070d9 reverse mode: implement -one-file-system
Fixes https://github.com/rfjakob/gocryptfs/issues/475
2021-08-16 19:23:58 +02:00
Jakob Unterwurzacher ad4b99170b tests/cli: escape filenames in TestBadname logs
I just got this message (not reproducible) with unescaped
binary garbage.

  UnmountErr: "/var/tmp/gocryptfs-test-parent-1026/114471933/TestMountBackground.899727687.mnt" was not found in MountInfo, cannot check for FD leaks
  UnmountErr: "/var/tmp/gocryptfs-test-parent-1026/114471933/TestConfigPipe.212912444.mnt" was not found in MountInfo, cannot check for FD leaks
  DecryptName "mzaZRF9_0IU-_5vv2wPC_i": unPad16 error: Padding too long, padLen=49 > 16
  OpenDir ".": invalid entry "KqQ346cuOAFHv_qSta5PhAwrongPattern": bad message
  DecryptName "mzaZRF9_0IU-_5vv2wP_in": unPad16 error: Padding byte at i=10 is invalid
  --- FAIL: TestBadname (0.11s)
      cli_test.go:885: Case 5 failed: 'KqQ346cuOAFHv_qSta5P_invalid_file GOCRYPTFS_BAD_NAME' in [file_invalid_file GOCRYPTFS_BAD_NAME,file,mzaZRF9_0IU-_5vv2wPC_invalid_file GOCRYPTFS_BAD_NAME,file GOCRYPTFS_BAD_NAME,�*A���y���Gfnvalid_file GOCRYPTFS_BAD_NAME,mzaZRF9_0IU-_5vv2wP_invalid_file GOCRYPTFS_BAD_NAME]
  Invalid cipherdir: directory /var/tmp/gocryptfs-test-parent-1026/114471933/TestInitNotEmpty not empty
  FAIL
  FAIL	github.com/rfjakob/gocryptfs/tests/cli	4.817s
2021-08-16 17:15:07 +02:00
Jakob Unterwurzacher 763499ee80 inomap: update outdated wording in comments 2021-08-16 17:14:14 +02:00
Jakob Unterwurzacher b8d78d6a31 inomap: warn on first use of spillMap
We normally should not need it, warn if we do.
As the tests run with -wpanic, we would catch it.
2021-08-16 17:13:14 +02:00
Jakob Unterwurzacher dd24fed532 Makefile: add uninstall target 2021-08-13 10:41:48 +02:00
Jakob Unterwurzacher 9a8dfd98ef main: accept magic /dev/fd/ mountpoint
https://github.com/rfjakob/gocryptfs/issues/590
2021-08-12 19:19:50 +02:00
Jakob Unterwurzacher 831e225616 syscallcompat: use BTRFS_SUPER_MAGIC from unix lib 2021-08-11 20:28:20 +02:00
Jakob Unterwurzacher 4e7efb1dff Makefile: don't ever run "git clean -dxff"
"make ci" almost made me lose the new quirks files.
Let's not do that anymore.
2021-08-11 20:23:46 +02:00
Jakob Unterwurzacher 2d386fc92e syscallcompat: move quirks logic here & fix darwin
We need to look at f_fstypename acc. to
https://stackoverflow.com/a/52299141/1380267 :

> As filesystem type numbers are now assigned at runtime in
> recent versions of MacOS, you must use f_fstypename to
> determine the type.

https://github.com/rfjakob/gocryptfs/issues/585
2021-08-11 20:23:35 +02:00
Jakob Unterwurzacher 0c16616117 main: add testcases for convertToDoubleDash & parseCliOpts 2021-08-10 19:42:33 +02:00
Jakob Unterwurzacher 463f6e8962 main: take advantage of pflag slice types
Our multipleStrings type is now built in.
2021-08-10 19:09:58 +02:00
Jakob Unterwurzacher 88f5e8d76e main: show specific error on command line parse failure 2021-08-10 19:09:12 +02:00
Jakob Unterwurzacher 527e72bf80 main: autoformat import block
The autoformatter now always wants to move the ensurefds012
import into the import block. Accept it and fix the spelling
of "alphabetically".
2021-08-10 18:28:27 +02:00
Jakob Unterwurzacher f53f52b046 main: switch from flag to pflag
Need support for flags at any position for
https://github.com/rfjakob/gocryptfs/issues/590
2021-08-10 18:24:35 +02:00
Jakob Unterwurzacher 8c9a1c1121 main: push TestPrefixOArgs testcase struct into TestPrefixOArgs
No need to have it declared globally.
2021-08-10 09:33:19 +02:00
Jakob Unterwurzacher c3c9513e65 fusefrontend: add quirks for MacOS ExFAT
This also moves the quirks logic into fusefrontend.

Fixes https://github.com/rfjakob/gocryptfs/issues/585
2021-08-02 20:01:26 +02:00
Jakob Unterwurzacher 75cf36fe7b go mod: upgrade go-fuse
Let's not crash anymore when we see inode number 1
( 0aaef6dde4 )

https://github.com/rfjakob/gocryptfs/issues/585
2021-08-02 19:16:53 +02:00
Jakob Unterwurzacher c6b0c777a1 README: Update Changelog 2021-07-31 17:48:44 +02:00
Jakob Unterwurzacher 1bc1db620b fusefrontend: -sharedstorage: present stable inode numbers
Use the Gen field (inode generation) to distinguish hard links
while passing the real inode numbers to userspace.

Fixes https://github.com/rfjakob/gocryptfs/issues/584
2021-07-31 13:24:25 +02:00
Jakob Unterwurzacher eecbcbb090 tests: matrix: add TestPwd
https://github.com/rfjakob/gocryptfs/issues/584
2021-07-31 13:23:05 +02:00
Jakob Unterwurzacher 1dfd6b7b76 fusefrontend: prepareAtSyscall: handle error when opening ourselves
Error handling was missing here, so we would later get confusing
EBADF errors due to dirfd being -1.
2021-07-31 10:53:32 +02:00
Jakob Unterwurzacher 0ca302f12a fusefrontend: implement fsync on directories
Fixes https://github.com/rfjakob/gocryptfs/issues/587
2021-07-29 20:39:50 +02:00
Jakob Unterwurzacher e83b79b4c2 fido2: actually drop `-v` flag
Commit 2a9d70d48f only
dropped the flag on mount but not on `-init`.

Also drop it on `-init`.

Fixes https://github.com/rfjakob/gocryptfs/issues/571 (part II)
2021-07-29 12:47:40 +02:00
Jakob Unterwurzacher 51bddd826e go mod: set version to 1.16 & drop explicit "-mod=vendor" from ci
This makes "go build" automatically use the vendor
directory, if present.

See https://golang.org/doc/modules/gomod-ref#go for details.

Up to now, we ignored the vendor dir completely!

Fixes https://github.com/rfjakob/gocryptfs/issues/581
2021-07-29 12:36:53 +02:00
Jakob Unterwurzacher 6f0ed4b8c4 github ci: Add Github Actions CI
Add Github Actions and delete defunct Travis CI.
2021-07-29 12:36:05 +02:00
Jakob Unterwurzacher db81614cd6 canonical-benchmarks.bash: handle relative paths
Passing a relative path was broken because we cd'ed
away first.
2021-06-27 11:48:41 +02:00
22 changed files with 278 additions and 378 deletions

View File

@ -60,7 +60,7 @@ func gcf_list_dir(sessionID int, dirName string) (*C.char, *C.int, C.int) {
var cachedIV []byte
if !OpenedVolumes[sessionID].plainTextNames {
// Read the DirIV from disk
cachedIV, err = nametransform.ReadDirIVAt(fd)
cachedIV, err = volume.nameTransform.ReadDirIVAt(fd)
if err != nil {
return nil, nil, 0
}

View File

@ -50,7 +50,7 @@ func (volume *Volume) openBackingDir(relPath string) (dirfd int, cName string, e
// Walk the directory tree
parts := strings.Split(relPath, "/")
for i, name := range parts {
iv, err := nametransform.ReadDirIVAt(dirfd)
iv, err := volume.nameTransform.ReadDirIVAt(dirfd)
if err != nil {
syscall.Close(dirfd)
return -1, "", err
@ -112,7 +112,7 @@ func (volume *Volume) prepareAtSyscall(path string) (dirfd int, cName string, er
// Cache store
if !volume.plainTextNames {
// TODO: openBackingDir already calls ReadDirIVAt(). Avoid duplicate work?
iv, err := nametransform.ReadDirIVAt(dirfd)
iv, err := volume.nameTransform.ReadDirIVAt(dirfd)
if err != nil {
syscall.Close(dirfd)
return -1, "", err

View File

@ -5,9 +5,7 @@ package configfile
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"syscall"
"os"
@ -60,64 +58,66 @@ type ConfFile struct {
filename string
}
// randBytesDevRandom gets "n" random bytes from /dev/random or panics
func randBytesDevRandom(n int) []byte {
f, err := os.Open("/dev/random")
if err != nil {
log.Panic("Failed to open /dev/random: " + err.Error())
}
defer f.Close()
b := make([]byte, n)
_, err = io.ReadFull(f, b)
if err != nil {
log.Panic("Failed to read random bytes: " + err.Error())
}
return b
// CreateArgs exists because the argument list to Create became too long.
type CreateArgs struct {
Filename string
Password []byte
PlaintextNames bool
LogN int
Creator string
AESSIV bool
DeterministicNames bool
XChaCha20Poly1305 bool
}
// Create - create a new config with a random key encrypted with
// "password" and write it to "filename".
// Uses scrypt with cost parameter logN.
func Create(filename string, password []byte, plaintextNames bool,
logN int, creator string, aessiv bool, devrandom bool, fido2CredentialID []byte, fido2HmacSalt []byte) error {
var cf ConfFile
cf.filename = filename
cf.Creator = creator
cf.Version = contentenc.CurrentVersion
// Set feature flags
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagGCMIV128])
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagHKDF])
if plaintextNames {
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagPlaintextNames])
// "Password" and write it to "Filename".
// Uses scrypt with cost parameter "LogN".
func Create(args *CreateArgs) error {
cf := ConfFile{
filename: args.Filename,
Creator: args.Creator,
Version: contentenc.CurrentVersion,
}
// Feature flags
cf.setFeatureFlag(FlagHKDF)
if args.XChaCha20Poly1305 {
cf.setFeatureFlag(FlagXChaCha20Poly1305)
} else {
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagDirIV])
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagEMENames])
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagLongNames])
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagRaw64])
// 128-bit IVs are mandatory for AES-GCM (default is 96!) and AES-SIV,
// XChaCha20Poly1305 uses even an even longer IV of 192 bits.
cf.setFeatureFlag(FlagGCMIV128)
}
if aessiv {
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagAESSIV])
}
if len(fido2CredentialID) > 0 {
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[FlagFIDO2])
cf.FIDO2 = &FIDO2Params{
CredentialID: fido2CredentialID,
HMACSalt: fido2HmacSalt,
if args.PlaintextNames {
cf.setFeatureFlag(FlagPlaintextNames)
} else {
if !args.DeterministicNames {
cf.setFeatureFlag(FlagDirIV)
}
cf.setFeatureFlag(FlagEMENames)
cf.setFeatureFlag(FlagLongNames)
cf.setFeatureFlag(FlagRaw64)
}
if args.AESSIV {
cf.setFeatureFlag(FlagAESSIV)
}
// Catch bugs and invalid cli flag combinations early
cf.ScryptObject = NewScryptKDF(args.LogN)
if err := cf.Validate(); err != nil {
return err
}
// Catch bugs and invalid cli flag combinations early
cf.ScryptObject = NewScryptKDF(args.LogN)
if err := cf.Validate(); err != nil {
return err
}
{
// Generate new random master key
var key []byte
if devrandom {
key = randBytesDevRandom(cryptocore.KeyLen)
} else {
key = cryptocore.RandBytes(cryptocore.KeyLen)
}
key := cryptocore.RandBytes(cryptocore.KeyLen)
// Encrypt it using the password
// This sets ScryptObject and EncryptedKey
// Note: this looks at the FeatureFlags, so call it AFTER setting them.
cf.EncryptKey(key, password, logN, false)
cf.EncryptKey(key, args.Password, args.LogN, false)
for i := range key {
key[i] = 0
}
@ -175,39 +175,22 @@ func Load(filename string) (*ConfFile, error) {
return nil, err
}
if cf.Version != contentenc.CurrentVersion {
return nil, fmt.Errorf("Unsupported on-disk format %d", cf.Version)
}
// Check that all set feature flags are known
for _, flag := range cf.FeatureFlags {
if !cf.isFeatureFlagKnown(flag) {
return nil, fmt.Errorf("Unsupported feature flag %q", flag)
}
}
// Check that all required feature flags are set
var requiredFlags []flagIota
if cf.IsFeatureFlagSet(FlagPlaintextNames) {
requiredFlags = requiredFlagsPlaintextNames
} else {
requiredFlags = requiredFlagsNormal
}
deprecatedFs := false
for _, i := range requiredFlags {
if !cf.IsFeatureFlagSet(i) {
fmt.Fprintf(os.Stderr, "Required feature flag %q is missing\n", knownFlags[i])
deprecatedFs = true
}
}
if deprecatedFs {
return nil, exitcodes.NewErr("Deprecated filesystem", exitcodes.DeprecatedFS)
if err := cf.Validate(); err != nil {
return nil, exitcodes.NewErr(err.Error(), exitcodes.DeprecatedFS)
}
// All good
return &cf, nil
}
func (cf *ConfFile) setFeatureFlag(flag flagIota) {
if cf.IsFeatureFlagSet(flag) {
// Already set, ignore
return
}
cf.FeatureFlags = append(cf.FeatureFlags, knownFlags[flag])
}
// DecryptMasterKey decrypts the masterkey stored in cf.EncryptedKey using
// password.
func (cf *ConfFile) DecryptMasterKey(password []byte, giveHash bool) (masterkey, scryptHash []byte, err error) {
@ -296,6 +279,9 @@ func (cf *ConfFile) GetMasterkey(password, givenScryptHash, returnedScryptHashBu
// then rename over "filename".
// This way a password change atomically replaces the file.
func (cf *ConfFile) WriteFile() error {
if err := cf.Validate(); err != nil {
return err
}
tmp := cf.filename + ".tmp"
// 0400 permissions: gocryptfs.conf should be kept secret and never be written to.
fd, err := os.OpenFile(tmp, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400)
@ -315,7 +301,7 @@ func (cf *ConfFile) WriteFile() error {
err = fd.Sync()
if err != nil {
// This can happen on network drives: FRITZ.NAS mounted on MacOS returns
// "operation not supported": https://github.com/rfjakob/gocryptfs/issues/390
// "operation not supported": https://github.com/rfjakob/gocryptfs/v2/issues/390
// Try sync instead
syscall.Sync()
}
@ -340,3 +326,18 @@ func getKeyEncrypter(scryptHash []byte, useHKDF bool) *contentenc.ContentEnc {
ce := contentenc.New(cc, 4096, false)
return ce
}
// ContentEncryption tells us which content encryption algorithm is selected
func (cf *ConfFile) ContentEncryption() (algo cryptocore.AEADTypeEnum, err error) {
if err := cf.Validate(); err != nil {
return cryptocore.AEADTypeEnum{}, err
}
if cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) {
return cryptocore.BackendXChaCha20Poly1305, nil
}
if cf.IsFeatureFlagSet(FlagAESSIV) {
return cryptocore.BackendAESSIV, nil
}
// If neither AES-SIV nor XChaCha are selected, we must be using AES-GCM
return cryptocore.BackendGoGCM, nil
}

View File

@ -11,7 +11,8 @@ const (
// This flag is mandatory since gocryptfs v1.0.
FlagEMENames
// FlagGCMIV128 indicates 128-bit GCM IVs.
// This flag is mandatory since gocryptfs v1.0.
// This flag is mandatory since gocryptfs v1.0,
// except when XChaCha20Poly1305 is used.
FlagGCMIV128
// FlagLongNames allows file names longer than 176 bytes.
FlagLongNames
@ -28,36 +29,26 @@ const (
// FlagFIDO2 means that "-fido2" was used when creating the filesystem.
// The masterkey is protected using a FIDO2 token instead of a password.
FlagFIDO2
// FlagXChaCha20Poly1305 means we use XChaCha20-Poly1305 file content encryption
FlagXChaCha20Poly1305
)
// knownFlags stores the known feature flags and their string representation
var knownFlags = map[flagIota]string{
FlagPlaintextNames: "PlaintextNames",
FlagDirIV: "DirIV",
FlagEMENames: "EMENames",
FlagGCMIV128: "GCMIV128",
FlagLongNames: "LongNames",
FlagAESSIV: "AESSIV",
FlagRaw64: "Raw64",
FlagHKDF: "HKDF",
FlagFIDO2: "FIDO2",
}
// Filesystems that do not have these feature flags set are deprecated.
var requiredFlagsNormal = []flagIota{
FlagDirIV,
FlagEMENames,
FlagGCMIV128,
}
// Filesystems without filename encryption obviously don't have or need the
// filename related feature flags.
var requiredFlagsPlaintextNames = []flagIota{
FlagGCMIV128,
FlagPlaintextNames: "PlaintextNames",
FlagDirIV: "DirIV",
FlagEMENames: "EMENames",
FlagGCMIV128: "GCMIV128",
FlagLongNames: "LongNames",
FlagAESSIV: "AESSIV",
FlagRaw64: "Raw64",
FlagHKDF: "HKDF",
FlagFIDO2: "FIDO2",
FlagXChaCha20Poly1305: "XChaCha20Poly1305",
}
// isFeatureFlagKnown verifies that we understand a feature flag.
func (cf *ConfFile) isFeatureFlagKnown(flag string) bool {
func isFeatureFlagKnown(flag string) bool {
for _, knownFlag := range knownFlags {
if knownFlag == flag {
return true

View File

@ -1,6 +1,7 @@
package configfile
import (
"fmt"
"log"
"math"
"os"
@ -61,8 +62,9 @@ func NewScryptKDF(logN int) ScryptKDF {
// DeriveKey returns a new key from a supplied password.
func (s *ScryptKDF) DeriveKey(pw []byte) []byte {
s.validateParams()
if err := s.validateParams(); err != nil {
os.Exit(exitcodes.ScryptParams)
}
k, err := scrypt.Key(pw, s.Salt, s.N, s.R, s.P, s.KeyLen)
if err != nil {
log.Panicf("DeriveKey failed: %v", err)
@ -80,21 +82,22 @@ func (s *ScryptKDF) LogN() int {
// If not, it exists with an error message.
// This makes sure we do not get weak parameters passed through a
// rougue gocryptfs.conf.
func (s *ScryptKDF) validateParams() {
func (s *ScryptKDF) validateParams() error {
minN := 1 << scryptMinLogN
if s.N < minN {
os.Exit(exitcodes.ScryptParams)
return fmt.Errorf("Fatal: scryptn below 10 is too low to make sense")
}
if s.R < scryptMinR {
os.Exit(exitcodes.ScryptParams)
return fmt.Errorf("Fatal: scrypt parameter R below minimum: value=%d, min=%d", s.R, scryptMinR)
}
if s.P < scryptMinP {
os.Exit(exitcodes.ScryptParams)
return fmt.Errorf("Fatal: scrypt parameter P below minimum: value=%d, min=%d", s.P, scryptMinP)
}
if len(s.Salt) < scryptMinSaltLen {
os.Exit(exitcodes.ScryptParams)
return fmt.Errorf("Fatal: scrypt salt length below minimum: value=%d, min=%d", len(s.Salt), scryptMinSaltLen)
}
if s.KeyLen < cryptocore.KeyLen {
os.Exit(exitcodes.ScryptParams)
return fmt.Errorf("Fatal: scrypt parameter KeyLen below minimum: value=%d, min=%d", s.KeyLen, cryptocore.KeyLen)
}
return nil
}

View File

@ -0,0 +1,67 @@
package configfile
import (
"fmt"
"../contentenc"
)
// Validate that the combination of settings makes sense and is supported
func (cf *ConfFile) Validate() error {
if cf.Version != contentenc.CurrentVersion {
return fmt.Errorf("Unsupported on-disk format %d", cf.Version)
}
// scrypt params ok?
if err := cf.ScryptObject.validateParams(); err != nil {
return err
}
// All feature flags that are in the config file are known?
for _, flag := range cf.FeatureFlags {
if !isFeatureFlagKnown(flag) {
return fmt.Errorf("Unknown feature flag %q", flag)
}
}
// File content encryption
{
switch {
case cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) && cf.IsFeatureFlagSet(FlagAESSIV):
return fmt.Errorf("Can't have both XChaCha20Poly1305 and AESSIV feature flags")
case cf.IsFeatureFlagSet(FlagAESSIV):
if !cf.IsFeatureFlagSet(FlagGCMIV128) {
return fmt.Errorf("AESSIV requires GCMIV128 feature flag")
}
case cf.IsFeatureFlagSet(FlagXChaCha20Poly1305):
if cf.IsFeatureFlagSet(FlagGCMIV128) {
return fmt.Errorf("XChaCha20Poly1305 conflicts with GCMIV128 feature flag")
}
if !cf.IsFeatureFlagSet(FlagHKDF) {
return fmt.Errorf("XChaCha20Poly1305 requires HKDF feature flag")
}
// The absence of other flags means AES-GCM (oldest algorithm)
case !cf.IsFeatureFlagSet(FlagXChaCha20Poly1305) && !cf.IsFeatureFlagSet(FlagAESSIV):
if !cf.IsFeatureFlagSet(FlagGCMIV128) {
return fmt.Errorf("AES-GCM requires GCMIV128 feature flag")
}
}
}
// Filename encryption
{
switch {
case cf.IsFeatureFlagSet(FlagPlaintextNames) && cf.IsFeatureFlagSet(FlagEMENames):
return fmt.Errorf("Can't have both PlaintextNames and EMENames feature flags")
case cf.IsFeatureFlagSet(FlagPlaintextNames):
if cf.IsFeatureFlagSet(FlagDirIV) {
return fmt.Errorf("PlaintextNames conflicts with DirIV feature flag")
}
if cf.IsFeatureFlagSet(FlagLongNames) {
return fmt.Errorf("PlaintextNames conflicts with LongNames feature flag")
}
if cf.IsFeatureFlagSet(FlagRaw64) {
return fmt.Errorf("PlaintextNames conflicts with Raw64 feature flag")
}
case cf.IsFeatureFlagSet(FlagEMENames):
// All combinations of DirIV, LongNames, Raw64 allowed
}
}
return nil
}

View File

@ -13,9 +13,6 @@ import (
"../stupidgcm"
)
// NonceMode determines how nonces are created.
type NonceMode int
const (
//value from FUSE doc
MAX_KERNEL_WRITE = 128 * 1024
@ -27,15 +24,6 @@ const (
// master key in the config file is encrypted with a 96-bit IV for
// gocryptfs v1.2 and earlier. v1.3 switched to 128 bit.
DefaultIVBits = 128
_ = iota // skip zero
// RandomNonce chooses a random nonce.
RandomNonce NonceMode = iota
// ReverseDeterministicNonce chooses a deterministic nonce, suitable for
// use in reverse mode.
ReverseDeterministicNonce NonceMode = iota
// ExternalNonce derives a nonce from external sources.
ExternalNonce NonceMode = iota
)
// ContentEnc is used to encipher and decipher file content.
@ -171,7 +159,7 @@ func (be *ContentEnc) DecryptBlock(ciphertext []byte, blockNo uint64, fileID []b
nonce := ciphertext[:be.cryptoCore.IVLen]
if bytes.Equal(nonce, be.allZeroNonce) {
// Bug in tmpfs?
// https://github.com/rfjakob/gocryptfs/issues/56
// https://github.com/rfjakob/gocryptfs/v2/issues/56
// http://www.spinics.net/lists/kernel/msg2370127.html
return nil, errors.New("all-zero nonce")
}

View File

@ -6,10 +6,11 @@ import (
"crypto/aes"
"crypto/cipher"
"crypto/sha512"
"fmt"
"log"
"runtime"
"golang.org/x/crypto/chacha20poly1305"
"github.com/rfjakob/eme"
"../siv_aead"
@ -17,37 +18,35 @@ import (
)
const (
// KeyLen is the cipher key length in bytes. 32 for AES-256.
// KeyLen is the cipher key length in bytes. All backends use 32 bytes.
KeyLen = 32
// AuthTagLen is the length of a GCM auth tag in bytes.
// AuthTagLen is the length of a authentication tag in bytes.
// All backends use 16 bytes.
AuthTagLen = 16
)
// AEADTypeEnum indicates the type of AEAD backend in use.
type AEADTypeEnum int
const (
// BackendOpenSSL specifies the OpenSSL backend.
BackendOpenSSL AEADTypeEnum = 3
// BackendGoGCM specifies the Go based GCM backend.
BackendGoGCM AEADTypeEnum = 4
// BackendAESSIV specifies an AESSIV backend.
BackendAESSIV AEADTypeEnum = 5
)
func (a AEADTypeEnum) String() string {
switch a {
case BackendOpenSSL:
return "BackendOpenSSL"
case BackendGoGCM:
return "BackendGoGCM"
case BackendAESSIV:
return "BackendAESSIV"
default:
return fmt.Sprintf("%d", a)
}
type AEADTypeEnum struct {
Name string
NonceSize int
}
// BackendOpenSSL specifies the OpenSSL backend.
// "AES-GCM-256-OpenSSL" in gocryptfs -speed.
var BackendOpenSSL AEADTypeEnum = AEADTypeEnum{"AES-GCM-256-OpenSSL", 16}
// BackendGoGCM specifies the Go based GCM backend.
// "AES-GCM-256-Go" in gocryptfs -speed.
var BackendGoGCM AEADTypeEnum = AEADTypeEnum{"AES-GCM-256-Go", 16}
// BackendAESSIV specifies an AESSIV backend.
// "AES-SIV-512-Go" in gocryptfs -speed.
var BackendAESSIV AEADTypeEnum = AEADTypeEnum{"AES-SIV-512-Go", siv_aead.NonceSize}
// BackendXChaCha20Poly1305 specifies XChaCha20-Poly1305-Go.
// "XChaCha20-Poly1305-Go" in gocryptfs -speed.
var BackendXChaCha20Poly1305 AEADTypeEnum = AEADTypeEnum{"XChaCha20-Poly1305-Go", chacha20poly1305.NonceSizeX}
// CryptoCore is the low level crypto implementation.
type CryptoCore struct {
// EME is used for filename encryption.
@ -58,7 +57,8 @@ type CryptoCore struct {
AEADBackend AEADTypeEnum
// GCM needs unique IVs (nonces)
IVGenerator *nonceGenerator
IVLen int
// IVLen in bytes
IVLen int
}
// New returns a new CryptoCore object or panics.
@ -71,10 +71,11 @@ type CryptoCore struct {
// a config file) or the masterkey (when finally mounting the filesystem).
func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDecode bool) *CryptoCore {
if len(key) != KeyLen {
log.Panic(fmt.Sprintf("Unsupported key length %d", len(key)))
log.Panicf("Unsupported key length of %d bytes", len(key))
}
if IVBitLen != 96 && IVBitLen != 128 && IVBitLen != chacha20poly1305.NonceSizeX*8 {
log.Panicf("Unsupported IV length of %d bits", IVBitLen)
}
// We want the IV size in bytes
IVLen := IVBitLen / 8
// Initialize EME for filename encryption.
var emeCipher *eme.EMECipher
@ -103,12 +104,14 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
if useHKDF {
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
gcmKey = append([]byte{}, key...)
}
switch aeadType {
case BackendOpenSSL:
if IVLen != 16 {
log.Panic("stupidgcm only supports 128-bit IVs")
if IVBitLen != 128 {
log.Panicf("stupidgcm only supports 128-bit IVs, you wanted %d", IVBitLen)
}
aeadCipher = stupidgcm.New(gcmKey, forceDecode)
case BackendGoGCM:
@ -116,7 +119,7 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
if err != nil {
log.Panic(err)
}
aeadCipher, err = cipher.NewGCMWithNonceSize(goGcmBlockCipher, IVLen)
aeadCipher, err = cipher.NewGCMWithNonceSize(goGcmBlockCipher, IVBitLen/8)
if err != nil {
log.Panic(err)
}
@ -125,9 +128,9 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
gcmKey[i] = 0
}
} else if aeadType == BackendAESSIV {
if IVLen != 16 {
// SIV supports any nonce size, but we only use 16.
log.Panic("AES-SIV must use 16-byte nonces")
if IVBitLen != 128 {
// SIV supports any nonce size, but we only use 128.
log.Panicf("AES-SIV must use 128-bit IVs, you wanted %d", IVBitLen)
}
// AES-SIV uses 1/2 of the key for authentication, 1/2 for
// encryption, so we need a 64-bytes key for AES-256. Derive it from
@ -144,16 +147,34 @@ func New(key []byte, aeadType AEADTypeEnum, IVBitLen int, useHKDF bool, forceDec
for i := range key64 {
key64[i] = 0
}
} else if aeadType == BackendXChaCha20Poly1305 {
// We don't support legacy modes with XChaCha20-Poly1305
if IVBitLen != chacha20poly1305.NonceSizeX*8 {
log.Panicf("XChaCha20-Poly1305 must use 192-bit IVs, you wanted %d", IVBitLen)
}
if !useHKDF {
log.Panic("XChaCha20-Poly1305 must use HKDF, but it is disabled")
}
derivedKey := hkdfDerive(key, hkdfInfoXChaChaPoly1305Content, chacha20poly1305.KeySize)
aeadCipher, err = chacha20poly1305.NewX(derivedKey)
if err != nil {
log.Panic(err)
}
} else {
log.Panic("unknown backend cipher")
log.Panicf("unknown cipher backend %q", aeadType.Name)
}
if aeadCipher.NonceSize()*8 != IVBitLen {
log.Panicf("Mismatched aeadCipher.NonceSize*8=%d and IVBitLen=%d bits",
aeadCipher.NonceSize()*8, IVBitLen)
}
return &CryptoCore{
EMECipher: emeCipher,
AEADCipher: aeadCipher,
AEADBackend: aeadType,
IVGenerator: &nonceGenerator{nonceLen: IVLen},
IVLen: IVLen,
IVGenerator: &nonceGenerator{nonceLen: IVBitLen / 8},
IVLen: IVBitLen / 8,
}
}

View File

@ -10,9 +10,10 @@ import (
const (
// "info" data that HKDF mixes into the generated key to make it unique.
// For convenience, we use a readable string.
hkdfInfoEMENames = "EME filename encryption"
hkdfInfoGCMContent = "AES-GCM file content encryption"
hkdfInfoSIVContent = "AES-SIV file content encryption"
hkdfInfoEMENames = "EME filename encryption"
hkdfInfoGCMContent = "AES-GCM file content encryption"
hkdfInfoSIVContent = "AES-SIV file content encryption"
hkdfInfoXChaChaPoly1305Content = "XChaCha20-Poly1305 file content encryption"
)
// hkdfDerive derives "outLen" bytes from "masterkey" and "info" using

View File

@ -22,7 +22,11 @@ const (
// ReadDirIVAt reads "gocryptfs.diriv" from the directory that is opened as "dirfd".
// Using the dirfd makes it immune to concurrent renames of the directory.
// Retries on EINTR.
func ReadDirIVAt(dirfd int) (iv []byte, err error) {
// If deterministicNames is set it returns an all-zero slice.
func (n *NameTransform) ReadDirIVAt(dirfd int) (iv []byte, err error) {
if n.deterministicNames {
return make([]byte, DirIVLen), nil
}
fdRaw, err := syscallcompat.Openat(dirfd, DirIVFilename,
syscall.O_RDONLY|syscall.O_NOFOLLOW, 0)
if err != nil {
@ -63,7 +67,7 @@ func WriteDirIVAt(dirfd int) error {
iv := cryptocore.RandBytes(DirIVLen)
// 0400 permissions: gocryptfs.diriv should never be modified after creation.
// Don't use "ioutil.WriteFile", it causes trouble on NFS:
// https://github.com/rfjakob/gocryptfs/commit/7d38f80a78644c8ec4900cc990bfb894387112ed
// https://github.com/rfjakob/gocryptfs/v2/commit/7d38f80a78644c8ec4900cc990bfb894387112ed
fd, err := syscallcompat.Openat(dirfd, DirIVFilename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, dirivPerms)
if err != nil {
return err

View File

@ -124,7 +124,7 @@ func (n *NameTransform) WriteLongNameAt(dirfd int, hashName string, plainName st
plainName = filepath.Base(plainName)
// Encrypt the basename
dirIV, err := ReadDirIVAt(dirfd)
dirIV, err := n.ReadDirIVAt(dirfd)
if err != nil {
return err
}

View File

@ -23,20 +23,22 @@ type NameTransform struct {
// on the Raw64 feature flag
B64 *base64.Encoding
// Patterns to bypass decryption
badnamePatterns []string
badnamePatterns []string
deterministicNames bool
}
// New returns a new NameTransform instance.
func New(e *eme.EMECipher, longNames bool, raw64 bool, badname []string) *NameTransform {
func New(e *eme.EMECipher, longNames bool, raw64 bool, badname []string, deterministicNames bool) *NameTransform {
b64 := base64.URLEncoding
if raw64 {
b64 = base64.RawURLEncoding
}
return &NameTransform{
emeCipher: e,
longNames: longNames,
B64: b64,
badnamePatterns: badname,
emeCipher: e,
longNames: longNames,
B64: b64,
badnamePatterns: badname,
deterministicNames: deterministicNames,
}
}

View File

@ -6,14 +6,14 @@ const (
// never chmod'ed or chown'ed.
//
// Group-readable so the FS can be mounted by several users in the same group
// (see https://github.com/rfjakob/gocryptfs/issues/387 ).
// (see https://github.com/rfjakob/gocryptfs/v2/issues/387 ).
//
// Note that gocryptfs.conf is still created with 0400 permissions so the
// owner must explicitly chmod it to permit access.
//
// World-readable so an encrypted directory can be copied by the non-root
// owner when gocryptfs is running as root
// ( https://github.com/rfjakob/gocryptfs/issues/539 ).
// ( https://github.com/rfjakob/gocryptfs/v2/issues/539 ).
dirivPerms = 0444
// Permissions for gocryptfs.longname.[sha256].name files.

View File

@ -19,6 +19,11 @@ const (
// KeyLen is the required key length. The SIV algorithm supports other lengths,
// but we only support 64.
KeyLen = 64
// NonceSize is the required nonce/IV length.
// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
NonceSize = 16
// Overhead is the number of bytes added for integrity checking
Overhead = 16
)
// New returns a new cipher.AEAD implementation.
@ -42,11 +47,11 @@ func new2(keyIn []byte) cipher.AEAD {
func (s *sivAead) NonceSize() int {
// SIV supports any nonce size, but in gocryptfs we exclusively use 16.
return 16
return NonceSize
}
func (s *sivAead) Overhead() int {
return 16
return Overhead
}
// Seal encrypts "in" using "nonce" and "authData" and appends the result to "dst"

View File

@ -14,7 +14,7 @@ import (
// 2) Is ARM64 && has AES instructions && Go is v1.11 or higher
// (commit https://github.com/golang/go/commit/4f1f503373cda7160392be94e3849b0c9b9ebbda)
//
// See https://github.com/rfjakob/gocryptfs/wiki/CPU-Benchmarks
// See https://github.com/rfjakob/gocryptfs/v2/wiki/CPU-Benchmarks
// for benchmarks.
func PreferOpenSSL() bool {
if BuiltWithoutOpenssl {
@ -26,7 +26,7 @@ func PreferOpenSSL() bool {
return false
}
// On the Apple M1, Go stdlib is faster than OpenSSL, despite cpu.ARM64.HasAES
// reading false: https://github.com/rfjakob/gocryptfs/issues/556#issuecomment-848079309
// reading false: https://github.com/rfjakob/gocryptfs/v2/issues/556#issuecomment-848079309
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
return false
}

View File

@ -224,7 +224,7 @@ func (g *StupidGCM) Open(dst, iv, in, authData []byte) ([]byte, error) {
if res != 1 {
// The error code must always be checked by the calling function, because the decrypted buffer
// may contain corrupted data that we are returning in case the user forced reads
if g.forceDecode == true {
if g.forceDecode {
return append(dst, buf...), ErrAuth
}
return nil, ErrAuth

View File

@ -1,52 +0,0 @@
// +build without_openssl
package stupidgcm
import (
"fmt"
"os"
"../exitcodes"
)
type StupidGCM struct{}
const (
// BuiltWithoutOpenssl indicates if openssl been disabled at compile-time
BuiltWithoutOpenssl = true
)
func errExit() {
fmt.Fprintln(os.Stderr, "gocryptfs has been compiled without openssl support but you are still trying to use openssl")
os.Exit(exitcodes.OpenSSL)
}
func New(_ []byte, _ bool) *StupidGCM {
errExit()
// Never reached
return &StupidGCM{}
}
func (g *StupidGCM) NonceSize() int {
errExit()
return -1
}
func (g *StupidGCM) Overhead() int {
errExit()
return -1
}
func (g *StupidGCM) Seal(_, _, _, _ []byte) []byte {
errExit()
return nil
}
func (g *StupidGCM) Open(_, _, _, _ []byte) ([]byte, error) {
errExit()
return nil, nil
}
func (g *StupidGCM) Wipe() {
errExit()
}

View File

@ -12,7 +12,7 @@ import (
// https://github.com/golang/go/blob/d2a80f3fb5b44450e0b304ac5a718f99c053d82a/src/os/file_posix.go#L243
//
// This is needed because CIFS throws lots of EINTR errors:
// https://github.com/rfjakob/gocryptfs/issues/483
// https://github.com/rfjakob/gocryptfs/v2/issues/483
//
// Don't use retryEINTR() with syscall.Close()!
// See https://code.google.com/p/chromium/issues/detail?id=269623 .

View File

@ -19,7 +19,7 @@ const sizeofDirent = int(unsafe.Sizeof(unix.Dirent{}))
// maxReclen sanity check: Reclen should never be larger than this.
// Due to padding between entries, it is 280 even on 32-bit architectures.
// See https://github.com/rfjakob/gocryptfs/issues/197 for details.
// See https://github.com/rfjakob/gocryptfs/v2/issues/197 for details.
const maxReclen = 280
type DirEntry struct {

View File

@ -1,7 +1,6 @@
package syscallcompat
import (
"bytes"
"syscall"
"golang.org/x/sys/unix"
@ -27,21 +26,6 @@ func Readlinkat(dirfd int, path string) (string, error) {
}
}
// Faccessat exists both in Linux and in MacOS 10.10+, but the Linux version
// DOES NOT support any flags. Emulate AT_SYMLINK_NOFOLLOW like glibc does.
func Faccessat(dirfd int, path string, mode uint32) error {
var st unix.Stat_t
err := Fstatat(dirfd, path, &st, unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
return err
}
if st.Mode&syscall.S_IFMT == syscall.S_IFLNK {
// Pretend that a symlink is always accessible
return nil
}
return unix.Faccessat(dirfd, path, mode, 0)
}
// Openat wraps the Openat syscall.
// Retries on EINTR.
func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error) {
@ -57,15 +41,6 @@ func Openat(dirfd int, path string, flags int, mode uint32) (fd int, err error)
return fd, err
}
// Fchownat syscall.
func Fchownat(dirfd int, path string, uid int, gid int, flags int) (err error) {
// Why would we ever want to call this without AT_SYMLINK_NOFOLLOW?
if flags&unix.AT_SYMLINK_NOFOLLOW == 0 {
flags |= unix.AT_SYMLINK_NOFOLLOW
}
return unix.Fchownat(dirfd, path, uid, gid, flags)
}
// Fstatat syscall.
// Retries on EINTR.
func Fstatat(dirfd int, path string, stat *unix.Stat_t, flags int) (err error) {
@ -100,118 +75,3 @@ const XATTR_BUFSZ = XATTR_SIZE_MAX + 1024
// We try with a small buffer first - this one can be allocated on the stack.
const XATTR_BUFSZ_SMALL = 500
// Fgetxattr is a wrapper around unix.Fgetxattr that handles the buffer sizing.
func Fgetxattr(fd int, attr string) (val []byte, err error) {
fn := func(buf []byte) (int, error) {
return unix.Fgetxattr(fd, attr, buf)
}
return getxattrSmartBuf(fn)
}
// Lgetxattr is a wrapper around unix.Lgetxattr that handles the buffer sizing.
func Lgetxattr(path string, attr string) (val []byte, err error) {
fn := func(buf []byte) (int, error) {
return unix.Lgetxattr(path, attr, buf)
}
return getxattrSmartBuf(fn)
}
func getxattrSmartBuf(fn func(buf []byte) (int, error)) ([]byte, error) {
// Fastpaths. Important for security.capabilities, which gets queried a lot.
buf := make([]byte, XATTR_BUFSZ_SMALL)
sz, err := fn(buf)
// Non-existing xattr
if err == unix.ENODATA {
return nil, err
}
// Underlying fs does not support security.capabilities (example: tmpfs)
if err == unix.EOPNOTSUPP {
return nil, err
}
// Small xattr
if err == nil && sz < len(buf) {
goto out
}
// Generic slowpath
//
// If the buffer is too small to fit the value, Linux and MacOS react
// differently:
// Linux: returns an ERANGE error and "-1" bytes.
// MacOS: truncates the value and returns "size" bytes.
//
// We choose the simple approach of buffer that is bigger than the limit on
// Linux, and return an error for everything that is bigger (which can
// only happen on MacOS).
buf = make([]byte, XATTR_BUFSZ)
sz, err = fn(buf)
if err == syscall.ERANGE {
// Do NOT return ERANGE - the user might retry ad inifinitum!
return nil, syscall.EOVERFLOW
}
if err != nil {
return nil, err
}
if sz >= XATTR_SIZE_MAX {
return nil, syscall.EOVERFLOW
}
out:
// Copy only the actually used bytes to a new (smaller) buffer
// so "buf" never leaves the function and can be allocated on the stack.
val := make([]byte, sz)
copy(val, buf)
return val, nil
}
// Flistxattr is a wrapper for unix.Flistxattr that handles buffer sizing and
// parsing the returned blob to a string slice.
func Flistxattr(fd int) (attrs []string, err error) {
// See the buffer sizing comments in getxattrSmartBuf.
// TODO: smarter buffer sizing?
buf := make([]byte, XATTR_BUFSZ)
sz, err := unix.Flistxattr(fd, buf)
if err == syscall.ERANGE {
// Do NOT return ERANGE - the user might retry ad inifinitum!
return nil, syscall.EOVERFLOW
}
if err != nil {
return nil, err
}
if sz >= XATTR_SIZE_MAX {
return nil, syscall.EOVERFLOW
}
attrs = parseListxattrBlob(buf[:sz])
return attrs, nil
}
// Llistxattr is a wrapper for unix.Llistxattr that handles buffer sizing and
// parsing the returned blob to a string slice.
func Llistxattr(path string) (attrs []string, err error) {
// TODO: smarter buffer sizing?
buf := make([]byte, XATTR_BUFSZ)
sz, err := unix.Llistxattr(path, buf)
if err == syscall.ERANGE {
// Do NOT return ERANGE - the user might retry ad inifinitum!
return nil, syscall.EOVERFLOW
}
if err != nil {
return nil, err
}
if sz >= XATTR_SIZE_MAX {
return nil, syscall.EOVERFLOW
}
attrs = parseListxattrBlob(buf[:sz])
return attrs, nil
}
func parseListxattrBlob(buf []byte) (attrs []string) {
parts := bytes.Split(buf, []byte{0})
for _, part := range parts {
if len(part) == 0 {
// Last part is empty, ignore
continue
}
attrs = append(attrs, string(part))
}
return attrs
}

View File

@ -35,7 +35,7 @@ func EnospcPrealloc(fd int, off int64, len int64) (err error) {
}
if err == syscall.EOPNOTSUPP {
// ZFS and ext3 do not support fallocate. Warn but continue anyway.
// https://github.com/rfjakob/gocryptfs/issues/22
// https://github.com/rfjakob/gocryptfs/v2/issues/22
return nil
}
return err

View File

@ -75,7 +75,7 @@ func registerNewVolume(rootCipherDir string, masterkey []byte, cf *configfile.Co
newVolume.cryptoCore = cryptocore.New(masterkey, cryptoBackend, contentenc.DefaultIVBits, true, forcedecode)
newVolume.contentEnc = contentenc.New(newVolume.cryptoCore, contentenc.DefaultBS, forcedecode)
var badname []string
newVolume.nameTransform = nametransform.New(newVolume.cryptoCore.EMECipher, true, true, badname)
newVolume.nameTransform = nametransform.New(newVolume.cryptoCore.EMECipher, true, true, badname, false)
//copying rootCipherDir
var grcd strings.Builder
@ -156,7 +156,16 @@ func gcf_change_password(rootCipherDir string, oldPassword, givenScryptHash, new
//export gcf_create_volume
func gcf_create_volume(rootCipherDir string, password []byte, plaintextNames bool, logN int, creator string) bool {
err := configfile.Create(filepath.Join(rootCipherDir, configfile.ConfDefaultName), password, plaintextNames, logN, creator, false, false, nil, nil)
err := configfile.Create(&configfile.CreateArgs{
Filename: filepath.Join(rootCipherDir, configfile.ConfDefaultName),
Password: password,
PlaintextNames: plaintextNames,
LogN: logN,
Creator: creator,
AESSIV: false,
DeterministicNames: false,
XChaCha20Poly1305: false,
})
if err == nil {
if plaintextNames {
return true