file icon logo

The algebraicfile specification v2

This document describes version 2 of the “algebraicfile” file format [1].

When the Algebraic app encrypts a file, the app saves file data, file metadata, and encryption parameters in a container file that follows this specification. The original file data and metadata can later be retrieved from the Algebraic encrypted file during decryption.

Each Algebraic encrypted file corresponds to a single original file.

Algebraic encrypted files typically use the extension “.algebraic”.

Versions

The versions of the specification are:

Version Link
1 https://algebraic.cc/algebraicfile/v1
2 https://algebraic.cc/algebraicfile/v2 (this page)

Changes from previous version

  • The secondary header section is now encrypted using XChaCha20-Poly1305, not XChaCha20. The authentication provided by the AEAD implementation provides a reliable way to detect whether the correct password was used during decryption.
  • The SecondaryHeaderLen field in the primary header section is now an int64, not a uint16. This allows storage of longer copyfile(3) data in the secondary header.

File structure

  Section Size Encrypted
1 Magic & version 6 bytes no
2 Primary header 57 bytes no
3 Secondary header variable length yes, XChaCha20-Poly1305
4 File data variable length (may be zero) yes, XChaCha20
5 Filler data variable length (may be zero) yes, XChaCha20
6 Checksum 32 bytes no

1. Magic & version

This structure of this section should remain the same for all versions of the algebraicfile specification. Reader implementations can read this section and then switch their parsing behavior based on the Version value.

The Go struct representation is below. The struct value is binary-encoded in big-endian order.

struct {
    Magic   [5]byte // value: [5]byte{'e', 'v', 'r', 'c', 'u'}
    Version uint8   // value: 0x02
}

Here is a hex dump of this section:

00000000: 6576 7263 7502

which decomposes into:

Magic                6576 7263 75
Version              02

2. Primary header

The Go struct representation is below. The struct value is binary-encoded in big-endian order.

struct {
    // Argon2id params.
    Salt    [16]byte
    Time    uint32 // number of passes over memory
    Mem     uint32 // KiB
    Threads uint8

    // XChaCha20 nonce.
    Nonce [24]byte

    // SecondaryHeaderLen is the length of the variable-length
    // secondary header that follows this section.
    SecondaryHeaderLen int64
}

For example, here is a hex dump of a primary header:

00000000: 1b2d 47d2 370b ca4e bed7 8378 3b64 878b
00000010: 0000 0002 0018 0000 048a 82d0 4960 0137
00000020: 8da1 2ed5 5e9e 946a 977d f0e6 d3cb 8fd5
00000030: bc00 0000 0000 0001 f0

which decomposes into:

Salt                 1b2d 47d2 370b ca4e bed7 8378 3b64 878b
Time                 0000 0002
Mem                  0018 0000
Threads              04
Nonce                8a82 d049 6001 378d a12e d55e 9e94 6a97 7df0 e6d3 cb8f d5bc
SecondaryHeaderLen   0000 0000 0000 01f0

3. Secondary header

The DataLen field records the length of the variable length file data that follows. The Copyfile field contains ACL and xattr data in AppleDouble format generated by copyfile(3). Note that the filler data length isn’t recorded. Reader implementations should consider all bytes after the file data but for the checksum to be filler data.

The Go struct representation is below. The struct value is JSON-encoded, then encrypted using XChaCha20-Poly1305.

struct {
    Copyfile    []byte `json:"cp,omitempty"` // packed copyfile(3) data
    DataLen     int64  `json:"dl,omitempty"` // uncompressed file data size; only for regular files
    Mode        uint32 `json:"m,omitempty"`  // fs.FileMode value
    Name        []byte `json:"n,omitempty"`
    Linkname    []byte `json:"l,omitempty"` // for symbolic links
    Uid         int64  `json:"u,omitempty"`
    Gid         int64  `json:"g,omitempty"`
    ModTime     int64  `json:"mt,omitempty"`
    AccessTime  int64  `json:"at,omitempty"`
    ChangeTime  int64  `json:"ct,omitempty"`
    BirthTime   int64  `json:"bt,omitempty"`
    Compression uint8  `json:"z,omitempty"` // compression used for data; currently only value is 0 (off)
}

4. File data

File data encrypted using XChaCha20. Variable length.

5. Filler data

Filler data. Variable length. The filler data is typically psuedo-randomly generated. Variable length.

This section is used to (optionally) obfuscate the file size of an encrypted file.

6. Checksum

SHA-256 sum of all preceding bytes.


  1. The exported type identifier on Apple systems is “org.littleroot.algebraic.algebraicfile”.