Help us shape the future of Go! Take the 2026 Go Developer Survey

e2edecrypt

package
v0.48.0 Latest Latest
Warning

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

Go to latest
Published: Sep 28, 2026 License: MIT Imports: 24 Imported by: 0

Documentation

Index

Constants

View Source
const (
	NamesAlg      = "AES-SIV-512"
	NamesEnc      = "b64url"
	NameKeyBytes  = 64
	NameMaxBytes  = 255
	DirIDBytes    = 16
	LongSuffix    = ".fxl"
	SidecarSuffix = ".fxl.name"
)

Name encryption constants, identical to the browser's.

View Source
const FxeExtension = ".fxe"

FxeExtension is the stored-name suffix of a single encrypted file.

View Source
const MarkerName = ".filex-e2e.json"

MarkerName is the marker's file name at an encrypted folder's root.

View Source
const (
	// StreamChunkLog2 is the chunk size writers use (2^20 = 1 MiB).
	StreamChunkLog2 = 20
)

Variables

View Source
var (
	// ErrWrongPassword: the password does not open the marker's verify blob.
	ErrWrongPassword = errors.New("wrong password")
	// ErrWrongRecoveryKey: malformed, wrong, or the folder has no recovery key.
	ErrWrongRecoveryKey = errors.New("wrong recovery key (or this folder has no recovery key)")
	// ErrNotMarker: the file is not a filex encrypted-folder marker.
	ErrNotMarker = errors.New("not a filex encrypted-folder marker (.filex-e2e.json)")
)
View Source
var ErrContent = errors.New("content failed authentication (damaged, tampered with, or from another folder)")

ErrContent: the content failed authentication — the file is damaged or was tampered with, or it belongs to another folder.

View Source
var ErrFileExists = errors.New("the output file already exists — pass another with -o")

ErrFileExists: the file a .fxe would decrypt to is already there.

View Source
var ErrNoMarker = errors.New("no " + MarkerName + " found in the input — pass the encrypted folder's root, or its marker with --marker")

ErrNoMarker: the input carries no .filex-e2e.json and none was given.

View Source
var ErrOutputExists = errors.New("the output directory already exists")

ErrOutputExists: the output directory is already there. Decrypting over it would mix old and new plaintext; pick another -o.

View Source
var FxeMagic = []byte("filexfxe")

FxeMagic starts every single encrypted file.

View Source
var MagicPrefix = []byte("filexe2e")

MagicPrefix starts every encrypted file.

Functions

func DecryptContent

func DecryptContent(fmk, data []byte) ([]byte, error)

DecryptContent opens one encrypted file with the folder master key.

func DecryptFileStream

func DecryptFileStream(w io.Writer, r io.Reader, fmk, previous []byte) error

DecryptFileStream decrypts one folder file — header version 0x01 or 0x02 — from r to w. previous is the prior folder key of a folder mid re-key, or nil. A 0x01 file is one AES-GCM message and is read whole (at most 200 MiB); a 0x02 file streams.

func DecryptFxe

func DecryptFxe(w io.Writer, r io.Reader, h *FxeHeader, k *FxeKeys) error

DecryptFxe writes the plaintext of a .fxe body (r positioned after the prefix) to w, checked end to end against the header's size.

func DecryptStream

func DecryptStream(w io.Writer, r io.Reader, dek, prefix []byte, log2 int, expect int64) (int64, error)

DecryptStream writes the plaintext of the STREAM body read from r to w. expect is the plaintext length the header promised, or -1. Any tampering — a flipped bit, a truncated, reordered or extended body, a length that disagrees with expect — returns an error wrapping ErrContent.

⚠ w has received the plaintext of every chunk before the failing one. The caller writes into a temporary place and throws it away on error.

func DirIDOf

func DirIDOf(stored string) []byte

DirIDOf returns the folder id a stored folder name carries, or nil.

func EncodedOf

func EncodedOf(stored string, readSidecar func(name string) ([]byte, bool)) (string, bool)

EncodedOf returns the encoded ciphertext a stored name carries — through its sidecar for a long one — or ok=false (a plain name, a sidecar, or a long name whose sidecar is missing or does not match).

func HasFxeMagic

func HasFxeMagic(b []byte) bool

HasFxeMagic reports whether b starts with the .fxe magic.

func HasMagic

func HasMagic(b []byte) bool

HasMagic reports whether b starts with the encrypted-file magic.

func ItemPrefixForSidecar

func ItemPrefixForSidecar(sidecar string) (string, bool)

ItemPrefixForSidecar returns the "<H>.fxl" an item next to this sidecar starts with (the item is "<H>.fxl" or "<H>.fxl.D").

func NameProblem

func NameProblem(name string) string

NameProblem mirrors the browser's namePlainProblem: "" when the name is one a local disk can hold, else why not.

func ParseRecoveryKey

func ParseRecoveryKey(s string) ([]byte, bool)

ParseRecoveryKey reads a typed recovery key back to its 20 bytes, forgiving case, dashes, spaces and the Crockford look-alikes (O→0, I/L→1).

func SidecarNameFor

func SidecarNameFor(stored string) (string, bool)

SidecarNameFor returns the sidecar that belongs to a long stored name (file or folder).

func StreamCiphertextSize

func StreamCiphertextSize(n int64, log2 int) int64

StreamCiphertextSize is the body length for a plaintext of n bytes.

func ValidChunkLog2

func ValidChunkLog2(n int) bool

ValidChunkLog2 reports whether a reader accepts this chunk size.

Types

type CorruptError

type CorruptError struct {
	Path string // plaintext path where known, with the stored name
	Err  error
}

CorruptError names the entry that stopped a run.

func (*CorruptError) Error

func (e *CorruptError) Error() string

func (*CorruptError) Unwrap

func (e *CorruptError) Unwrap() error

type EncryptedName

type EncryptedName struct {
	Stored         string // the entry's name on the server
	Encoded        string // the encoded ciphertext S (without a folder's ".D")
	SidecarName    string // set only for a long name
	SidecarContent string
}

EncryptedName is what the browser writes for one plaintext name.

type FxeHeader

type FxeHeader struct {
	// contains filtered or unexported fields
}

FxeHeader is a parsed .fxe header.

func ParseFxeHeader

func ParseFxeHeader(data []byte) (*FxeHeader, error)

ParseFxeHeader reads the JSON header with the browser's rules (parseFxeHeader). An unknown `req` entry returns an *UnsupportedError.

func ReadFxePrefix

func ReadFxePrefix(r io.Reader) (*FxeHeader, error)

ReadFxePrefix reads magic, version, length and header from r, leaving r at the first byte of the body.

func (*FxeHeader) HasRecovery

func (h *FxeHeader) HasRecovery() bool

HasRecovery reports whether the file has a recovery key slot.

func (*FxeHeader) Size

func (h *FxeHeader) Size() int64

Size is the plaintext length the header promises.

func (*FxeHeader) Unlock

func (h *FxeHeader) Unlock(secret string, recovery bool) (*FxeKeys, error)

Unlock opens the header with the password, or with the recovery key when recovery is true. A header whose key or name does not open under the unlocked file key is damage (a *CorruptError), not a wrong password.

type FxeKeys

type FxeKeys struct {
	DEK  []byte
	Name string
}

FxeKeys is what an unlock hands back: the STREAM key and the original name.

type Job

type Job struct {
	Marker *Marker
	// contains filtered or unexported fields
}

Job is one decryption, between Open and Close.

func Open

func Open(in string, opts OpenOptions) (*Job, error)

Open resolves the input and parses its marker. The returned Job must be closed. An *UnsupportedError comes back with a nil Job.

func (*Job) Close

func (j *Job) Close()

Close removes anything Open staged (an extracted zip).

func (*Job) IsFxe

func (j *Job) IsFxe() bool

IsFxe reports whether the input is a single encrypted file (.fxe).

func (*Job) Out

func (j *Job) Out() string

Out is where Run will write. For a .fxe without -o it is only known after Unlock (the original name comes out of the header); until then it is "".

func (*Job) Run

func (j *Job) Run() (*Result, error)

Run decrypts into the output directory, all or nothing.

func (*Job) Unlock

func (j *Job) Unlock(secret string, recovery bool) error

Unlock opens the marker with the password, or with the recovery key when recovery is true.

type Keys

type Keys struct {
	FMK      []byte
	Names    *NameKey
	Previous []byte
}

Keys is what an unlock hands back: the FMK, for a names-encrypted folder the name key, and for a folder mid re-key the PREVIOUS FMK — what the files not re-wrapped yet are still under.

type Marker

type Marker struct {
	V      int
	Salt   []byte
	Iter   int
	Verify string
	Fmk    string // "" (v1), "kek" or "wrapped"
	FmkPw  string
	Rk     *RecoverySlot
	Req    []string
	Names  *NamesSlot
	// Rekey is the previous FMK sealed under the current one, while a re-key
	// (a password change that replaced the folder key) is re-wrapping the
	// files' keys. Feature "rekey"; nil otherwise.
	Rekey *RekeySlot
	// ConvPending: an existing folder is being encrypted in place (feature
	// "conv"); files it has not reached yet are still plaintext.
	ConvPending bool
}

Marker is a parsed `.filex-e2e.json`.

func ParseMarker

func ParseMarker(data []byte) (*Marker, error)

ParseMarker reads a marker with the browser's rules (parseMarkerDetailed). A well-formed v3 marker that requires an unknown feature returns an *UnsupportedError; anything malformed returns ErrNotMarker.

func (*Marker) HasNames

func (m *Marker) HasNames() bool

HasNames reports whether file and folder names are encrypted.

func (*Marker) UnlockPassword

func (m *Marker) UnlockPassword(password string) (*Keys, error)

UnlockPassword derives the KEK (PBKDF2-SHA256), proves it against the verify blob, and returns the FMK (+ name key).

func (*Marker) UnlockRecoveryKey

func (m *Marker) UnlockRecoveryKey(recoveryKey string) (*Keys, error)

UnlockRecoveryKey opens the user-recovery slot and returns the FMK (+ name key).

type NameKey

type NameKey struct {
	Long   int    // shortening threshold the folder was created with
	RootID []byte // the encrypted root's own folder id
	// contains filtered or unexported fields
}

NameKey is a folder's unwrapped name key.

func NewNameKey

func NewNameKey(raw []byte, long int, rootID []byte) (*NameKey, error)

NewNameKey imports a raw 64-byte name key and the root's 16-byte id.

func (*NameKey) DecryptEncoded

func (k *NameKey) DecryptEncoded(encoded string, parentID []byte) (string, bool)

DecryptEncoded decrypts an encoded ciphertext name sealed for the folder with id parentID. ok is false when it is not one.

func (*NameKey) DecryptStoredName

func (k *NameKey) DecryptStoredName(stored string, parentID []byte, readSidecar func(name string) ([]byte, bool)) (string, NameState)

DecryptStoredName reads one stored name found in the folder with id parentID. readSidecar returns a sibling sidecar's content, or ok=false when there is no such file; it is only called for a long name.

func (*NameKey) DeriveDirID

func (k *NameKey) DeriveDirID(parentID []byte, plain string) []byte

DeriveDirID is the id of a folder whose name was never encrypted: a keyed PRF of its parent's id and its name.

func (*NameKey) EffectiveDirID

func (k *NameKey) EffectiveDirID(parentID []byte, stored string) []byte

EffectiveDirID is the id the CHILDREN of a folder are sealed under: the one its stored name carries, or — a plaintext-named folder — the derived one.

func (*NameKey) EncryptName

func (k *NameKey) EncryptName(plain string, parentID, dirID []byte) (EncryptedName, error)

EncryptName encrypts a plaintext name for the folder with id parentID, exactly as the browser does; dirID is the entry's own id when it is a folder, nil for a file. The decryptor does not need it; tests use it to build folders.

func (*NameKey) RecoverMoved

func (k *NameKey) RecoverMoved(stored string, here []byte, ids [][]byte, readSidecar func(name string) ([]byte, bool)) (string, bool)

RecoverMoved tries a name sealed for ANOTHER folder of this encrypted folder (an entry moved outside filex): every id in ids but here. ok=false when none opens it.

type NameKind

type NameKind int

NameKind is what a stored name is, from its spelling alone.

const (
	KindPlain NameKind = iota
	KindFile
	KindDir
	KindLong
	KindLongDir
	KindSidecar
)

type NameState

type NameState int

NameState is the outcome of reading one stored name.

const (
	NameDecrypted  NameState = iota // was encrypted, now plaintext
	NamePlain                       // was never encrypted; kept as stored
	NameUnreadable                  // ours by its spelling, did not decrypt here
	NameSidecar                     // bookkeeping; skip it
)

type NamesSlot

type NamesSlot struct {
	Alg  string `json:"alg"`
	Enc  string `json:"enc"`
	Long int    `json:"long"`
	Key  string `json:"key"`
	// RootID is the encrypted root's own folder id (16 bytes, base64url):
	// the associated data of every name directly inside the root.
	RootID  string `json:"root_id"`
	Pending bool   `json:"pending,omitempty"`
}

NamesSlot is the marker's encrypted-names slot (v3, feature "names").

type OpenOptions

type OpenOptions struct {
	// Out is the output directory. Empty: "<input>-decrypted" next to it
	// (".zip" dropped).
	Out string
	// MarkerPath is an explicit .filex-e2e.json, for an input that does not
	// carry its own (a subfolder, a single file, a zip made below the root).
	MarkerPath string
	// GOOS overrides runtime.GOOS for the output-name rules (tests).
	GOOS string
}

OpenOptions configure Open.

type RecoverySlot

type RecoverySlot struct {
	Salt string `json:"salt"`
	Blob string `json:"blob"`
}

RecoverySlot is the marker's user-recovery-key slot.

type RekeySlot

type RekeySlot struct {
	From    string `json:"from"`
	Pending bool   `json:"pending"`
}

RekeySlot is the marker's re-key-in-progress slot (v3, feature "rekey").

type Result

type Result struct {
	Out      string
	Files    int
	Dirs     int
	Warnings []string
}

Result summarises a finished run.

func DecryptTree

func DecryptTree(in string, opts OpenOptions, secret string, recovery bool) (*Result, error)

DecryptTree is Open + Unlock + Run + Close.

type StoredName

type StoredName struct {
	Kind    NameKind
	Hash    string // long, longdir, sidecar: the 43-character hash they share
	Encoded string // file, dir: the encoded ciphertext S
	DirID   []byte // dir, longdir: the folder's id
}

StoredName is a classified stored name.

func ClassifyStoredName

func ClassifyStoredName(stored string) StoredName

ClassifyStoredName reports what a stored name is, without a key.

type UnsupportedError

type UnsupportedError struct{ Features []string }

UnsupportedError: the folder requires a feature this build does not know. Opening it anyway is how an old client breaks a folder, so it is refused.

func (*UnsupportedError) Error

func (e *UnsupportedError) Error() string

Jump to

Keyboard shortcuts

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