e2edecrypt

package
v0.49.0 Latest Latest
Warning

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

Go to latest
Published: Sep 29, 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