Package Collection Signing

Introduction

Since Package Collections (SE-0291) can be created and published by anyone, issues related to authenticity and integrity may emerge. We would like to propose signed package collections such that:

  • Publishers can establish authenticity and protect their collections from tampering.
  • Users can choose and consume collections based on trust guarantees and therefore help prevent package collections from being used as an attack vector.

Package collection signing is optional—publishers are not required to sign collections. Users will be warned and prompted for confirmation before they can add an unsigned collection.

Tooling will be provided, most likely an addition to the existing generator repository, for publishers to sign their collections. It will be open-sourced and cross-platform, and publishers are strongly recommended to use this tool instead of attempting their own.

Detailed Design

Sign a package collection

To generate a signature one must provide:

  • The package collection (JSON) to be signed
  • A code signing certificate (DER encoded)
  • The certificate’s private key (PEM encoded)
  • The certificate’s chain in its entirety

The tool will produce a new collection JSON file with the signature embedded:

{
  // Package collection JSON
  ...,
  // New "signature" object
  "signature": {
    "signature": <SIGNATURE>,
    "certificate": {
      "subject": {
        "commonName": "Jane Doe"
      },
      "issuer": {
        "commonName": "Sample CA"
      }
    }
  }
}

The signature will include the certificate’s public key and chain for verification purposes.

Publish a package collection

Package collections, whether they are signed or not, must be accessible without requiring authentication, and their URLs should be HTTPS. This can be done by publishing the files to a web server or CDN-like infrastructure. Services such as GitHub.com, Amazon S3, and others also support the required features for hosting package collections.

Non-HTTPS collection URLs are allowed but will trigger user warnings similar to unsigned collections.

Verify package collection signature

If signature is present in a package collection, SwiftPM will verify that:

  • The file content (excluding signature ) is what was used to generate the signature. In other words, this checks to see if the collection has been altered since it was signed.
  • The signing certificate is valid.

SwiftPM will not import a collection if there were any failures during verification.

Certificate for signing

The following is enforced during signing and verification:

  • The timestamp at which signing/verification is done must fall within the signing certificate’s validity period.
  • The certificate’s “Extended Key Usage” extension must include “Code Signing”.
  • The certificate must use either 256-bit EC (recommended) or 2048-bit RSA key.
  • The certificate must not be revoked. The certificate authority must support OCSP, which means the certificate must have the “Certificate Authority Information Access” extension that includes OCSP as a method, specifying the responder’s URL.
  • The certificate chain is valid and root certificate must be trusted.

On Apple platforms, all root certificates that come preinstalled with the OS are automatically trusted. Users may specify additional certificates to trust by placing them in a directory and configure the signing tool or SwiftPM to use it. On non-Apple platforms, the signing tool and SwiftPM will only trust root certificates in the configured directory.

Non-expired, non-revoked Apple Distribution certificates from developer.apple.com satisfy all of the criteria above and can be used without further actions by collection publishers or users on Apple platforms.

11 Likes

Why the need for any certificate?

If using ECDSA (or EdDSA) we just need:

  1. The signature of the hash (typically SHA2-256) of JSON content (on R || S format).
  2. The public key of the private key used to sign

Yupp. That’s it.

So what am I missing here? :slight_smile:

1 Like

I agree, something simpler should be used. Managing client certificates is a major pain, just ask anyone still using a push service which doesn't support the new JWT APIs. Unless SPM is going to automate everything, there has to be a better alternative.

2 Likes

A certificate identifies who the signer is, whereas a key on its own doesn't unless we manage the set of keys.

Is that really relevant though? I mean I’m currently using lots of OSS from people I don’t know the identity of. That has been the case for a long time - and it works.

For me, and I guess for many devs, it is not important/relevant to know the identity of a fellow dev, but it would be nice to know that a commit, release or repo is indeed developed by her - whoever she is.

The case of GPG does exactly this, I upload my public key to Github and every commit I sign is marked as “Verified” on Github. But there are no certificates involved - which drastically reduces complexity.

Certificate Authorities are just that: authorities. Centralized entities of trust, which feels like something more and more people are interested in avoid: hence the rising popularity of decentralized organization and currencies within the crypto world.

A more off topic - semi-crazy idea: if the key used to sign the package collection was a key compatibility with a crypto currency eg Bitcoin or Ethereum, I would actually trust the signer more than any CA if the public Bitcoin/Ethereum address derived from the public key contained funds. Thus I know that the developers are REALLY interested in keeping the private key used to sign the Package Collection ultra super safe; because else they would loose funds. That I would really trust, and it is fully decentralized.

4 Likes

This is an interesting proposal, @yim_lee. A few questions that I had after reading it:

  • If all connections enforce TLS and package collections are consumed directly by Swift Package Manager, what protections do signatures provide? Is the goal to provide an additional layer of protection for data at rest?

  • How does this feature relate to Apple's Certificate Transparency policy? Would Swift Package Manager reject certificates that fail to meet the requirements of that policy?

  • If the signature is appended to the package collection payload, how exactly are the contents of that payload validated? I ask this because I suspect that things like insignificant whitespace and key reordering may be problematic for verification. Should signatures be based on JSON Canonical Form?

Exactly, it would be nice to know that a package collection is indeed created by someone as they claim. A key, on its own, doesn't tell us the "who", and we need to rely on something else to provide us that information. I agree that certificates are not necessary if there is a service that manages the key to identity mappings (such as GitHub in your GPG key example), but in our use-case we are not tying to any of such services.

Yes, plus a signature can be used to check who really created the package collection.

SwiftPM rejects certificates based on the criteria listed above. IIUC, the policy you link might impact collection downloads since it pertains to TLS, but that would be a system level issue and not just SwiftPM.

This is an excellent point and thanks for the link. The signing tool (that we will provide) will definitely apply those rules. i.e., it will not just use the collection JSON file/string "as-is" for the signature.

1 Like

This is now also a formal proposal on Swift Evolution: [Amendment] SE-0291: Package Collection Signing

1 Like