spacemeshos / SMIPS

Spacemesh Improvement Proposals
https://spacemesh.io
Creative Commons Zero v1.0 Universal
7 stars 1 forks source link

Updates Part IV: code signing #36

Open lrettig opened 3 years ago

lrettig commented 3 years ago

Regardless of how we ultimately decide to communicate and transmit upgrades to users, and the UX around downloading and installing updates (see #32, #33, #34), we'll need a scheme that allows the developers to sign those updates and users to validate the signature of an update for authenticity and integrity.

The very simplest option here would be to sign the binary release files for each respective platform using PKI already built into that platform, e.g., using Apple code signing (as we already do) for our macOS release. This has the advantage of not requiring any additional steps or infrastructure, but it has several significant downsides: it's totally inflexible as it allows the use of only a single set of credentials, a fixed PKI, and a proprietary signing tool (that isn't open source), it may not be easy to verify these signatures in our own software, it requires use of the official, OS-supported install tool (which likely requires administrator credentials), it may cost money to establish an identity, this infrastructure will vary from platform to platform, there is no easy way for the end user to verify the signatures manually, etc.

Note that, regardless of what ancillary code signing mechanism we ultimately choose, we'll additionally need to make use of the OS-specific code signing mechanism for platforms that require it, most notably macOS, but the two are not mutually exclusive and indeed are quite orthogonal for our purposes.

Design goals:

Non-goals:

Observations:

Proposal: We make use of GitHub PKI and sign releases using multiple of these existing, publicly verifiable keys. The signature that accompanies a release can be as simple as signing the checksum of the release file multiple times, e.g.:

SIGNATURE = ID_1_PUBKEY + ID_2_PUBKEY + SIGN(ID_1_PRIVKEY, SIGN(ID_2_PRIVKEY, SHA256SUM(release)))

We could optionally use something more sophisticated such as Schnorr signatures but this is probably unnecessary as it adds complexity and doesn't get us any closer to our design goals.

A release can be signed trivially by having two or more developers sign the checksum of the release binary once it's ready. This scheme is quite flexible and can support any number of signatures performed in any order.

An end user can verify the release by independently calculating the checksum, verifying that the public keys of the signers are valid, and verifying that the checksum has been correctly signed. Validity of the pubkeys in question can be ascertained in several ways: one or more of them can be "pinned" into the verification tool (a chain of trust could even be established in this way, such that previous keys could sign new ones), and/or the user can verify that they match the correct profiles on GitHub. A key can be revoked by removing it from a developer's GitHub profile.

tal-m commented 3 years ago

I think auto-update is crucial (in the initial phase of the network), but it's also a potential vulnerability that could have huge consequences if exploited, so it needs to be secured appropriately.

Using the platform code-signing API is potentially dangerous because it implicitly trusts the platform developers (e.g., Apple can generate their own "Spacemesh" certificate and sign it).

I'm also not sure about just having two developers sign the release to enable auto-update. I was thinking of something like 2-out-of-3 or 3-out-of-5 signing keys that are held in cold storage or on secure hardware devices.

In terms of the technical infrastructure, I think it's fine to use Github public key formats, so these keys can be published on Github. They probably shouldn't be one of the developer's regularly-used keys, however. (Also, the fact that the keys are also registered on Github shouldn't matter to our update verification code.)

We can even have the Github releases page as one of the update URLs. I don't think we should count on this as the only update source, however --- we don't want a Github crash (which isn't that rare, unfortunately) to prevent us from distributing an update).

I was thinking of having a list of several update URLs on different domains (so a single DNS server crash would also not affect operation), and serving the files from CDNs different cloud providers. The updates are static files, so this shouldn't be very difficult or expensive...