Package Format
Introduction
The application manager uses a very simple package format: a standard UNIX gzip-compressed TAR archive. Metadata is embedded as normal files, but using the reserved name prefix --PACKAGE-
. Even though USTAR tar archives support a lot of features, the application manager only supports standard files and directories with relative paths only (using ../
in a path is not allowed). Modes, except the owner's x
bit, are ignored.
This makes it very easy to write custom packagers as well as custom app-store server backends, since TAR archive handling is available as a utility library in any programming language.
Note: gzip is currently hardcoded, but since the (de)compression is done by libarchive, this could easily be replaced by bzip2 or xz compression. Both would lead to additional 3rd-party dependencies on Windows though.
These are the important files in a package:
File | Description |
---|---|
--PACKAGE-HEADER-- | Exactly one YAML document that needs to be the first file in the package - see below for a format description. This file is used as format selector. |
info.yaml | The manifest of the application. The packaging tool needs to make sure that this file is within the first 10 entries of the tar archive. See Manifest Definition for the documentation of the file format. |
icon.png | The icon of the application that is used in the app-store. Can also be used as the app's icon in the launcher menu on the device: see info.yaml. The packaging tool needs to make sure that this file is within the first 10 entries of the tar archive. |
--PACKAGE-FOOTER-- | One or more YAML document(s) that need to be the last file(s) in the package - see below for a format description. If adding more than one footer (e.g. appending the store signature by the app-store server), an arbitrary string can be appended to the file name (e.g. --PACKAGE-FOOTER--storesig ). This makes it easier to troubleshoot packages, since you can then easily extract them with the standard tar command line tool. |
In short, here is what is NOT allowed inside a package:
- You are not allowed to have file names that start with
--PACKAGE-
- You are not allowed to have absolute paths
- You are not allowed to have
../
in relative paths - You are not allowed to have sym-links, hard-links, device files, sockets, etc.
- File modes except the owner's
x
bit are ignored.
The Checksum Algorithm
All of package data, as well as important meta-data is protected by a SHA256 digest. This checksum has two purposes: it protects against transmission errors, and the package signing is done by encrypting this checksum.
The actual calculation of this checksum is done as follows:
- For every file that is not a
--PACKAGE-*
meta-data file, the complete content is added to the digest. Afterwards the following UTF-8 encoded string is also added to the digest:"F/<file size in bytes>/<file path>"
(e.g. the filefoo/test.qml
with a size of 250 bytes would generateF/250/foo/test.qml
) - For every directory, the following UTF-8 encoded string is added to the digst:
"D/0/<directory path>"
(e.g. the directoryfoo/images
would generateD/0/foo/images
)
The generated digest is put into the --PACKAGE-FOOTER--
as a 32-byte hex-encoded string.
The Signing Algorithm
The package format currently supports two signatures: a developer signature (generated by the developer before uploading to an appstore-server) and a store signature (generated by the appstore-server before the client actually downloads a package).
Both signatures are calculated the same way and are appended to the package's meta-data the same way: The binary digest is used as an input to create a detached PKCS7 signature. This PKCS7 signature is then BASE64 encoded and stored in --PACKAGE-FOOTER--
- either in the developerSignature
or storeSignature
field.
If you want to implement this signing algorithm yourself, then please look at these four existing implementations:
- C++ OpenSSL based in
src/crypto-lib/signature_openssl.cpp
- C++ WinCrypt based in
src/crypto-lib/signature_win.cpp
(Windows only) - C++ SecurityFramework based in
src/crypto-lib/signature_macos.cpp
(macOS only) - Python m2crypt based in the appstore-server reference implementation.
The Header and Footer file format
The fields within the --PACKAGE-FOOTER--
and --PACKAGE-HEADER--
file headers (first YAML document) are as follows:
Field Name | Type | Description |
---|---|---|
formatVersion | int | Required. Currently always 2 . |
formatType | string | Required. Always am-package-header for headers or am-package-footer for footers. |
The fields within the --PACKAGE-FOOTER--
and --PACKAGE-HEADER--
data (second YAML document) are as follows:
Field Name | Type | Description |
---|---|---|
id | string | The unique ID of the package. This has to match the ID of the package as described by the encompaning info.yaml manifest file. |
icon | string | Required. The icon's file name. The file must be located in the same directory as info.yaml and can be in any image format that Qt supports. |
Note: The old format (pre 5.14) had the formatVersion header field set to 1
and used the field name applicationId
instead of packageId
.
Example Package
This is an example of a minimal QML application package. The actual package can be created by tar'ing all these files up with
tar cvzf qmlapp.appkg ./--PACKAGE-HEADER-- info.yaml icon.png main.qml ./--PACKAGE-FOOTER--
File | Contents |
---|---|
--PACKAGE-HEADER-- | %YAML 1.1 --- formatType: am-package-header formatVersion: 2 --- packageId: com.pelagicore.minimal diskSpaceUsed: 1000 |
info.yaml | %YAML 1.1 --- formatType: am-package formatVersion: 1 --- id: 'com.pelagicore.minimal' icon: 'icon.png' name: en: 'Minimal App' applications: - id: com.pelagicore.minimalapp code: 'main.qml' runtime: 'qml' |
icon.png | a standard png image |
main.qml | your QML application |
--PACKAGE-FOOTER-- | %YAML 1.1 --- formatType: am-package-footer formatVersion: 2 --- # SHA256 digest in 32 char hex representation digest: '9df5635bb50e93846954c6377468c07835119e2835475ec90b3e9a9f7261cf27' |
© 2019 Luxoft Sweden AB. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.