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:

FileDescription
--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.yamlThe manifest of the application. The packaging tool needs to make sure that this file is within the first 10 entries of the tar archive.
icon.pngThe 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 file foo/test.qml with a size of 250 bytes would generate F/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 directory foo/images would generate D/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_osx.cpp (OS X only)
  • Python m2crypt based in the appstore-server reference implementation.

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--
FileContents
--PACKAGE-HEADER--
%YAML 1.1
---
formatType: am-package-header
formatVersion: 1
---
applicationId: com.pelagicore.minimal
diskSpaceUsed: 1000
info.yaml
%YAML 1.1
---
formatType: am-application
formatVersion: 1
---
id: 'com.pelagicore.minimal'
icon: 'icon.png'
name:
  en: 'Minimal App'
code: 'main.qml'

runtime: 'qml'
icon.pnga standard png image
main.qmlyour QML application
--PACKAGE-FOOTER--
%YAML 1.1
---
formatType: am-package-footer
formatVersion: 1
---
# SHA256 digest in 32 char hex representation
digest: '9df5635bb50e93846954c6377468c07835119e2835475ec90b3e9a9f7261cf27'

© 2018 Pelagicore AG. 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.