Skip to content

Accept app bundles instead of APK sets while retaining Accrescent's security properties #53

Description

@lberrymage

Background

Accrescent's backend (specifically Parcelo) accepts app uploads in the APK set format which at a high level contains:

  • Developer-signed split APKs (and standalone APKs if appropriate)
  • A table of contents (a binary BuildApksResult protobuf message)

To ensure uploaded apps are installable and otherwise valid, we run custom validation steps such as verifying that all APKs in the APK set are signed with the same certificate and have the same application ID. We also parse certain security-sensitive data such as application ID and version code from APKs directly instead of from the table of contents because the table of contents could be modified by a developer before upload. However, this approach of accepting APK sets and parsing them directly has significant limitations, namely:

  • Developers must upload APK sets which are often significantly larger than their corresponding app bundles, using more upload bandwidth, increasing storage usage, and causing more upload timeout errors compared to other app stores
  • We must write custom validation code for APK sets since bundletool doesn't have any (whereas it does for app bundles), which is very error-prone due to the large number of properties to check in the table of contents
  • In practice, our APK set validation code is incomplete, allowing developers to e.g. upload an app which is publishable but which clients can't install due to a tampered-with table of contents

We initially chose to accept APK sets because they include developer-signed APKs, whereas app bundles traditionally require generating APKs signed by the app store they're uploaded to — a requirement we cannot accept to ensure Accrescent's security properties. However, there is a way we can inherit all of the benefits of app bundles while still serving only developer-signed APKs.

Proposal

Instead of APK sets, we should accept a new, custom package format which contains at a minimum the following elements:

  • An Android app bundle
  • Split APK signatures

App developers could create this package from an app bundle by performing the following steps:

  1. Generate signed APKs in an APK set using bundletool
  2. Extract APK signatures from the signed APKs
  3. Compile the original app bundle and the APK signatures from step 2 into the custom package format

Accrescent's servers could process the new package format by following these steps:

  1. Generate unsigned APKs in an APK set from the uploaded app bundle using bundletool
  2. Copy APK signatures from the uploaded package and patch them onto the unsigned APKs
  3. Use apksigner to verify that the signatures of the now-signed APKs are valid

Adopting this process would solve all of the aforementioned issues with accepting APK sets directly, that is:

  • Developers would need to upload only app bundles and a small amount of extra metadata, saving large amounts of upload bandwidth, reducing storage usage, and reducing the number of upload timeout errors developers encounter
  • We can leverage bundletool's robust app bundle validation code, saving us significant code complexity
  • Our package validation can be comprehensive by allowing us to trust the APK set table of contents since we would generate it ourselves

In this way, we can accept app bundles as an app upload format while still keeping APKs signed by their respective developers.

Considerations

The proposed process for re-producing valid developer-signed APKs from an app bundle depends on bundletool's APK set output being reproducible, which isn't guaranteed. However, it appears to be reproducible from some basic testing, meaning we could rely upon this data at least for specific versions of bundletool. If future versions of bundletool generate different APKs than previous versions, we can generate APKs server-side using a compatible bundletool version to compensate. If future versions of bundletool generate different APKs across invocations, we can find where the discrepancies are and encode that metadata into our custom package format so that the server can modify server-generated APKs appropriately before adding the developer's signature.

A custom package format requires a custom tool. Before requiring developers to upload the new package format, we should develop, at a minimum, an Accrescent CLI which can generate valid Accrescent packages from an app bundle. We could improve the DX of this tool in the future by developing a Gradle plugin and other developer integrations.

Tasks

To implement this proposal, we would need to execute at least the following steps:

  • Design a custom package format which meets our requirements
  • Develop a custom library capable of extracting APK signatures and applying them to other APKs
  • Develop an Accrescent CLI which is capable of generating an Accrescent package from an app bundle
  • Make necessary adjustments to Accrescent's servers to handle the new package format (TBD)
  • Update our documentation to account for the change in package format

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions