An ODT file is an OpenDocument package: a ZIP file with a few extra rules, a manifest that lists its files, and optional encryption, signatures and RDF metadata. This page follows part 2 of OpenDocument 1.3 (OASIS, 27 April 2021); the XML files inside are described on the document structure page.
Files in the package
| Path | Required | Contents |
|---|---|---|
mimetype | Should be present; must be if the manifest has a / entry | The media type, application/vnd.oasis.opendocument.text, in ASCII, no line end. |
META-INF/manifest.xml | Yes | The manifest. |
content.xml | In practice | The body of the document. |
styles.xml | In practice | Styles, page layouts, master pages. |
meta.xml | No | Title, author, dates, statistics. |
settings.xml | No | Application settings such as the view. |
manifest.rdf | Only with RDF metadata | The metadata manifest. |
Thumbnails/thumbnail.png | Should be present unless encrypted | The preview image. |
META-INF/…signatures… | No | Digital signatures; the file name contains signatures, usually documentsignatures.xml. |
Pictures/, Object 1/ and others | No | Images, and embedded objects as sub-documents in their own folders, each with its own content.xml and so on. |
The document’s own files sit in the root of the ZIP. A folder that holds another
complete document (an embedded chart or formula, for example) is a sub-document.
Thumbnails/, Pictures/, Configurations2/ and
similar folder names are conventions of the programs that write ODT, not of the
specification, apart from the thumbnail’s path.
ZIP rules
- Every entry is stored (method 0) or deflated (method 8). No other compression.
mimetypeis the first entry, stored, with no extra field in its local header. This puts the media type at a fixed place:PKat offset 0, the namemimetypeat offset 30, and the media type at offset 38.- Encrypted entries are deflated first, then encrypted, and marked as stored; their
sizes in the ZIP headers are the encrypted sizes, and the real size goes in the
manifest’s
manifest:size. - A conforming package has no files under
META-INF/other thanmanifest.xmland signature files. An extended package may have others. - Other files may refer to each other with relative IRIs, resolved from the folder of
the file that contains the reference. A reference starting with
/or a scheme is resolved against the package’s own base IRI.
The first 77 bytes of an ODT file
00000000 50 4B 03 04 vv vv 00 00 00 00 tt tt dd dd 5E C6 PK..............
00000010 32 0C 27 00 00 00 27 00 00 00 08 00 00 00 6D 69 2.'...'.......mi
00000020 6D 65 74 79 70 65 61 70 70 6C 69 63 61 74 69 6F metypeapplicatio
00000030 6E 2F 76 6E 64 2E 6F 61 73 69 73 2E 6F 70 65 6E n/vnd.oasis.open
00000040 64 6F 63 75 6D 65 6E 74 2E 74 65 78 74 document.text
| Offset | Bytes | Field | Value |
|---|---|---|---|
| 0 | 50 4B 03 04 | Local file header signature | PK\3\4 |
| 4 | vv vv | Version needed to extract | Varies with the writer (for example 10 or 20). |
| 6 | 00 00 | Flags | None: no data descriptor, no encryption. |
| 8 | 00 00 | Compression method | 0, stored. |
| 10 | tt tt dd dd | Modification time and date | MS-DOS format; varies. |
| 14 | 5E C6 32 0C | CRC-32 | 0x0C32C65E, the CRC of the 39-byte media type. |
| 18 | 27 00 00 00 | Compressed size | 39. |
| 22 | 27 00 00 00 | Uncompressed size | 39. |
| 26 | 08 00 | File name length | 8. |
| 28 | 00 00 | Extra field length | 0, as the specification requires. |
| 30 | 6D 69 … 65 | File name | mimetype |
| 38 | 39 bytes | File data | application/vnd.oasis.opendocument.text |
A template (.ott) has application/vnd.oasis.opendocument.text-template
there instead, and a master document (.odm)
application/vnd.oasis.opendocument.text-master.
Manifest
META-INF/manifest.xml lists every file in the package with its media
type, except mimetype itself and files under META-INF/. It
has exactly one manifest:file-entry per file, and should have one with
the path / for the document itself, whose media type must equal the
contents of mimetype. Folders get an entry only if they hold a
sub-document. The manifest is never encrypted. Its namespace is
urn:oasis:names:tc:opendocument:xmlns:manifest:1.0.
<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.3">
<manifest:file-entry manifest:full-path="/" manifest:version="1.3" manifest:media-type="application/vnd.oasis.opendocument.text"/>
<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="settings.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="Thumbnails/thumbnail.png" manifest:media-type="image/png"/>
<manifest:file-entry manifest:full-path="Pictures/logo.png" manifest:media-type="image/png"/>
</manifest:manifest>
Elements
| Element | In | Attributes | Meaning |
|---|---|---|---|
manifest:manifest | (root) | manifest:version | The manifest. Contains manifest:encrypted-key and manifest:file-entry elements. |
manifest:file-entry | manifest:manifest | full-path, media-type, preferred-view-mode, size, version | One file or sub-document folder. May contain manifest:encryption-data. |
manifest:encryption-data | file-entry | checksum, checksum-type | How to decrypt the file. Contains algorithm, start-key-generation and key-derivation. |
manifest:algorithm | encryption-data | algorithm-name, initialisation-vector | The cipher. Empty for Blowfish; for an XML Encryption algorithm, may hold that algorithm’s parameters. |
manifest:start-key-generation | encryption-data | start-key-generation-name, key-size | How the start key is made from the password (UTF-8 bytes). Absent means the defaults: SHA1, 20 bytes. |
manifest:key-derivation | encryption-data | key-derivation-name, iteration-count, key-size, salt | How the file’s key is derived from the start key. |
manifest:encrypted-key | manifest:manifest | – | An encryption key encrypted for a known recipient (OpenPGP). Contains encryption-method, keyinfo and CipherData. New in 1.3. |
manifest:encryption-method | encrypted-key | PGPAlgorithm | The algorithm that encrypted the key. |
manifest:keyinfo | encrypted-key | – | About the recipient’s key. Contains PGPData. |
manifest:PGPData | keyinfo | – | Contains PGPKeyID and PGPKeyPacket. |
manifest:PGPKeyID | PGPData | – | The OpenPGP key ID (RFC 4880), in base64. |
manifest:PGPKeyPacket | PGPData | – | An OpenPGP key material packet, in base64. |
manifest:CipherData | encrypted-key | – | Contains CipherValue. Unlike XML Signature’s, it cannot refer to data elsewhere. |
manifest:CipherValue | CipherData | – | The encrypted key, in base64. |
Attributes
| Attribute | Type | Meaning and values |
|---|---|---|
manifest:version (on manifest) | string | The version of the manifest format; must be 1.3 in ODF 1.3 (1.2 in ODF 1.2). |
manifest:version (on file-entry) | string | The format version of the file or sub-document, read according to its media type; for the / entry, the ODF version. |
manifest:full-path | string | The path in the ZIP, as written in the central directory; / is the package itself. The manifest and the ZIP may use different text encodings. |
manifest:media-type | string | The MIME type of the file, or of the document in a folder. Should be present wherever a type exists; may be empty. |
manifest:preferred-view-mode | token | Only on the / entry: edit, presentation-slide-show, read-only, or a namespaced token for an implementation’s own view. |
manifest:size | non-negative integer | Required for encrypted files: the size before compression and encryption. |
manifest:checksum-type | SHA1/1K or IRI | Digest used to check the password: SHA1/1K or urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#sha1-1k (SHA-1 of the first 1,024 bytes of the compressed, unencrypted file); …manifest:1.0#sha256-1k (SHA-256 of the same); an XML Encryption digest IRI; or, in extended packages, another IRI. |
manifest:checksum | base64 | That digest. It confirms a decryption worked; it is not a security feature. |
manifest:algorithm-name | Blowfish CFB or IRI | The cipher: Blowfish CFB or urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#blowfish (Blowfish, 8-bit CFB); an XML Encryption block cipher IRI such as http://www.w3.org/2001/04/xmlenc#aes256-cbc; or, in extended packages, another IRI. |
manifest:initialisation-vector | base64 | The initialisation vector, in the length the cipher needs. |
manifest:start-key-generation-name | SHA1 or IRI | Hash of the password that gives the start key: SHA1 (default), http://www.w3.org/2000/09/xmldsig#sha1, http://www.w3.org/2000/09/xmldsig#sha256 (recommended), or another XML Encryption digest IRI. |
manifest:key-size | non-negative integer | Length in bytes of the key produced: default 20 for the start key, 16 for the derived key. |
manifest:key-derivation-name | PBKDF2, PGP or IRI | PBKDF2 or …manifest:1.0#pbkdf2: PBKDF2 with HMAC-SHA-1 (RFC 2898), which then needs a start-key-generation; PGP for keys in encrypted-key; or an implementation’s IRI. |
manifest:iteration-count | non-negative integer | PBKDF2 iterations (1,024 in the legacy scheme). |
manifest:salt | base64 | The random salt for the key derivation, any length (16 bytes in the legacy scheme). |
manifest:PGPAlgorithm | IRI | Implementation-defined identifier of the OpenPGP algorithm. |
Encryption
Some or all files can be encrypted with a password. For each encrypted file:
- The start key is a hash of the password’s UTF-8 bytes
(
start-key-generation): SHA-1, 20 bytes, in the legacy scheme; SHA-256 in current ones. - A key for the file is derived from the start key with PBKDF2, using a new random
salt for each file (
key-derivation): HMAC-SHA-1, 1,024 iterations, 16-byte salt and 128-bit key in the legacy scheme. - The file is deflated, then encrypted with that key and a random initialisation
vector (
algorithm): Blowfish in 8-bit CFB mode with an 8-byte vector in the legacy scheme; most files written since about 2011 use AES-256 in CBC mode, named by its XML Encryption IRI.
The checksum is taken over the first 1,024 bytes of the deflated, unencrypted data, so a reader can tell a wrong password before inflating. A conforming writer must support Blowfish CFB and PBKDF2; readers must support those and the SHA1/1K and SHA-256/1K checksums. Thumbnails of encrypted documents must not show their content.
<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml" manifest:size="2803">
<manifest:encryption-data manifest:checksum-type="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#sha256-1k" manifest:checksum="…">
<manifest:algorithm manifest:algorithm-name="http://www.w3.org/2001/04/xmlenc#aes256-cbc" manifest:initialisation-vector="…"/>
<manifest:start-key-generation manifest:start-key-generation-name="http://www.w3.org/2000/09/xmldsig#sha256" manifest:key-size="32"/>
<manifest:key-derivation manifest:key-derivation-name="PBKDF2" manifest:key-size="32" manifest:iteration-count="100000" manifest:salt="…"/>
</manifest:encryption-data>
</manifest:file-entry>
The key sizes and iteration count in this example are typical of files from LibreOffice, not values fixed by the specification; read them from the manifest. Writers may also use other, implementation-defined algorithms in extended packages, so a reader should check the names before trying to decrypt.
Digital signatures
Signatures are XML files under META-INF/ whose names contain
signatures. LibreOffice writes documentsignatures.xml for the
document and macrosignatures.xml for macros. The root element is
dsig:document-signatures (namespace
urn:oasis:names:tc:opendocument:xmlns:digitalsignature:1.0) with the
attribute dsig:version="1.2", holding any number of W3C XML Signature
ds:Signature elements.
- Each
ds:Signaturehas a uniqueIdand ads:KeyInfo, which should hold ads:X509Datawith the signing certificate’s issuer and serial number and the certificate itself. - A
ds:Referencewith noType, or with the typehttp://docs.oasis-open.org/office/v1.2/OS/OpenDocument-v1.2.odt, names a file in the package by its path. One with the typehttp://uri.etsi.org/01903#SignedPropertiespoints at XAdES signed properties. - Only canonicalisation transforms may be applied to files in the package.
- The signing time is recorded in a
ds:SignaturePropertyholding a Dublin Coredc:date, or asxades:SigningTime, or both. - XAdES (ETSI TS 101 903) may be used; if so, a
xades:SigningCertificateis required and time stamps are DER-encoded. - Encrypt first, then sign (usual): the signatures are not encrypted and cover the encrypted files. Sign first, then encrypt: the signatures are encrypted too.
RDF metadata
A document may hold RDF/XML metadata files, listed in manifest.rdf in the
root (or in a sub-document’s folder). It uses the ontology
http://docs.oasis-open.org/ns/office/1.2/meta/pkg#:
| Term | Kind | Meaning |
|---|---|---|
pkg:Document | Class | The document or sub-document itself. |
pkg:File | Class | A file in the package, identified by a relative IRI. |
pkg:MetadataFile | Class | A metadata file; a kind of pkg:File. |
pkg:Element | Class | An XML element in a file of the package (one with an xml:id). |
pkg:hasPart | Property | Of a pkg:Document: a file that belongs to it. |
pkg:mimeType | Property | Of a pkg:File: its media type. |
manifest.rdf need not exist when there is no RDF metadata. References to
files of the same package inside metadata are relative IRIs.
Thumbnail
Unless the document is encrypted, writers should store a preview of its first page as
Thumbnails/thumbnail.png, a PNG with no
frame, shadow or border. The specification sets no size but suggests one that looks
right when scaled into a 256 × 256 square. An encrypted document must not have a preview of its content,
though it may have an unencrypted generic one.