fscrypt

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 14, 2025 License: MIT Imports: 4 Imported by: 0

README

fscrypt

gocryptfs implementation in Go fs.FS interface, as a library.

Synopsis

package fscrypt // import "codeberg.org/NaitLee/fscrypt"

func New(base any, password []byte) (fs.FS, error)
func NewFS(base any, password []byte) (*internal.FS, error)

func Create(base any, password []byte) ([]byte, error)
func CreateReverse(base any, password []byte) ([]byte, error)
func Passwd(base any, oldpass, newpass, masterkey []byte) ([]byte, error)

func ReadPassword(fd int) []byte
func ParseMasterKey(input []byte) []byte
func PrintMasterKey(key []byte, indent string, stdout, stderr io.Writer)

All current gocryptfs ciphers are supported: AES-GCM, AES-SIV, XChaCha20-Poly1305.

The FS is in “forward mode.”

Example valid “base” FS:

Examples

See cmd directory in this repository for example programs.

They can be used daily. You can install them if you like:

go install codeberg.org/NaitLee/fscrypt/cmd/...@latest
go install codeberg.org/NaitLee/fscrypt/cmd/fscrypt-9p@latest
export PATH="$HOME/go/bin:$PATH"
fscrypt-ls -l /tmp/crypt

Code Snippets

mkdir /tmp/crypt
gocryptfs -init /tmp/crypt
# or
fscrypt-init /tmp/crypt
import "codeberg.org/NaitLee/fscrypt"

fmt.Print("Password: ")
pw := fscrypt.ReadPassword(0)
fs_, err := fscrypt.New(os.DirFS("/tmp/crypt"), pw)
data, err := fs.ReadFile(fs_, "file.txt")
fmt.Println(string(data))

Use Cases

Compatibility

  • fscrypt doesn’t support “old” gocryptfs file systems, as they are insecure and complicates code.
  • Specifically, these FeatureFlags are required.
    ["HKDF", "DirIV", "EMENames", "LongNames", "Raw64"]
    
    If they are not found in your gocryptfs.conf, then you need to upgrade it using latest gocryptfs, possibly by creating a new gocryptfs and re-encrypting your files.
  • For forward mode with -aessiv, gocryptfs is not deterministic, but fscrypt is, as intended in reverse mode.

Bugs

  • No reverse mode at the moment. (You can create one with CreateReverse, but can’t use.)
  • Symlinks not yet supported.
  • This package is not audited by experts for potential security issues.
  • Only native Go implementations are used for the ciphers. (i.e. OpenSSL backend not available; it seems to be slower on my machine anyway)
  • Code is not thoroughly tested. Always backup your data. No warranty!

Trivia

“Not to be confused with github.com/google/fscrypt

  • Just before I publish this package, I’ve found that there’s also a “fscrypt” under github.com/google. I can’t come up with a better name so let’s continue using this name.
  • gocryptfs code about file encryption is “internal”, and depends on fuse, which makes it hard to be used as a library, so I decided to write one by myself.
  • The code here is more compact, which makes it easier or harder to understand, depending on one’s experience.
  • os.DirFS can be escaped with ..; os.Root.FS prevents that. Use os.OpenRoot if you can.
  • Despite fscrypt.New accepts any base implementing OpenFile(...) (*os.File, error), the resulting FS implements OpenFile(...) (fs.File, error), as the returned file is a virtual file, which can’t be made a real *os.File.
  • You can use duckfs to compat the fs differences, but as it’s too dynamic, it shouldn’t be overused.

Licensed under MIT/Expat.

See go.mod for libraries in use.

Feel free to use the idea.

Documentation

Index

Constants

View Source
const DefaultLog2N = 16

Default log2n parameter for Create and CreateReverse.

Variables

View Source
var (
	ErrInvalidFS    = internal.ErrInvalidFS
	ErrBadPassword  = internal.ErrBadPassword
	ErrDirNotEmpty  = internal.ErrDirNotEmpty
	ErrMustBeAesSiv = internal.ErrMustBeAesSiv
	ErrNotDirectory = internal.ErrNotDirectory
)

Common fscrypt errors.

View Source
var (
	ErrBadDirIv           = internal.ErrBadDirIv
	ErrBadKeySize         = internal.ErrBadKeySize
	ErrUnsupportedVersion = internal.ErrUnsupportedVersion
	ErrUnknownAeadBackend = internal.ErrUnknownAeadBackend
)

Extra fscrypt errors.

Functions

func Create added in v0.2.0

func Create(base any, password []byte) ([]byte, error)

Create a new gocryptfs.

"base" should be a writable FS-like object, like os.Root. Use os.OpenRoot to get one.

Returns the master key. Print it to user in hex then wipe it.

func CreateFS added in v0.2.0

func CreateFS(base any, aeadType AeadType, log2n int, password, masterkey []byte, reverse bool) ([]byte, error)

Advanced gocryptfs creation.

The default AeadType in gocryptfs is AeadTypeGcm.

log2n is log_2 scrypt N value, 10 <= log2n <= 28. The recommended value is 16 (DefaultLog2N).

"masterkey" should be nil, unless you know what you are doing.

Be sure to wipe the returned master key.

If "reverse" is true, "aeadType" must be AeadTypeSiv.

func CreateReverse added in v0.2.0

func CreateReverse(base any, password []byte) ([]byte, error)

Create a new "reverse mode" gocryptfs.

"base" should be a writable FS-like object, like os.Root. Use os.OpenRoot to get one.

Returns the master key. Print it to user in hex then wipe it.

func MasterKey added in v0.2.0

func MasterKey(base any, password []byte) ([]byte, error)

Get master key of a certain FS. Make sure to wipe the returned key.

Should only be used in specialized tools.

func New

func New(base any, password []byte) (fs.FS, error)

New fscrypt instance. It implements fs.FS, fs.ReadDirFS, fs.StatFS.

"base" can be fs.FS or os.Root with gocryptfs content inside.

You can get a "base" by using os.DirFS or os.OpenRoot. Other fs.FS implementation also works.

Files opened implements fs.File and io.ReaderAt.

See also NewFS, [NewDuckFS], and ReadPassword.

May return ErrBadPassword, fs.ErrInvalid, or other errors.

func NewFS added in v0.2.0

func NewFS(base any, password []byte) (*internal.FS, error)

New fscrypt instance, but returning pointer to internal.FS struct.

There are more public functions than fs.FS, but they are unstable. For a stable interface wrapper, use [duckfs.FS].

If your "base" has an OpenFile function (like os.Root.OpenFile), you can call internal.FS.OpenFile or internal.FS.Create, and the resulting file implements io.Writer, io.WriterAt.

The similar applies for internal.FS.Mkdir, etc.

For other writing functions like Chmod, use relative functions of your "base" and internal.FS.EncryptName.

func NewWithMasterKey added in v0.2.0

func NewWithMasterKey(base any, masterkey []byte) (*internal.FS, error)

New fscrypt instance with explicit master key, useful for rescuing the file system.

Only use this in specialized tools.

func ParseMasterKey added in v0.2.0

func ParseMasterKey(input []byte) []byte

Parse master key from user input, which is hex and may contain misc chars (dash, space, newline, quote).

Returns raw master key, make sure to wipe it.

May return nil indicating invalid input, like less than 64 hex digits.

func Passwd added in v0.2.0

func Passwd(base any, oldpass, newpass, masterkey []byte) ([]byte, error)

Change password and/or masterkey of a gocryptfs.

"masterkey" can be nil, or an AeadTypeGcm key (32 bytes).

Be cautious supplying "masterkey", as changing master key doesn't require "oldpass", and a bad master key can break a fs irreversibly.

Automatically identifies reverse mode fs.

Returns the master key, print it to user once and wipe it.

func PrintMasterKey added in v0.2.0

func PrintMasterKey(key []byte, indent string, stdout, stderr io.Writer)

Print the master key to stdout, and formatting (indent and dash) to stderr.

00000000-00000000-00000000-00000000-
00000000-00000000-00000000-00000000

Indent can be:

"\n    "

Suggested io params: os.Stdout, os.Stderr, io.Discard, *os.File.

func ReadPassword

func ReadPassword(fd int) []byte

Read password from commandline, hiding user input if possible.

Only fd 0 (stdin) is supported at the moment.

You may want to use golang.org/x/term.ReadPassword instead.

Be sure to wipe the returned password after use:

pw := fscrypt.ReadPassword(0)
fs_, err := fscrypt.New(os.DirFS("/tmp/crypt"), pw)
// wipe the password just after use, even before checking error from [New]
for i := range pw {
	pw[i] = 0
}
if err != nil {
	panic(err)
}

Types

type AeadType added in v0.2.0

type AeadType int
const (
	AeadTypeGcm AeadType = iota
	AeadTypeSiv
	AeadTypeXChaCha20Poly1305
)

AEAD types.

Directories

Path Synopsis
cmd
fscrypt-cat command
fscrypt-http command
fscrypt-init command
fscrypt-ls command
fscrypt-passwd command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL