Identity/FormatUpgrade
Contents
Format-Upgrade Analysis
To safely deploy changes in any of the serialized browserid data formats (serialized public keys, IdP records, certificates, and signed assertions), we need either be sure that the eventual recipient of these messages will be able to understand them, or we need to be able to detect when they cannot and somehow fall back to an older version.
The message flow in BrowserID looks roughly like the following:
Recipients
There are three bits of code that receive serialized messages (and must understand them for everything to work). The first is the IdP provisioning backend which creates signed certificates, labeled "Step 1". This must understand the browser's pubkey format ("B"). The secondary/fallback-IdP has the same requirements.
The second ("Step 2") is the browser's assertion-signing code, which needs to parse the signed certificate ("C") to learn when it expires (at which point it must go back to the IdP for a new certificate.
The third is the RP's verifier ("Step 3"), either the online service that we run, or a local implementation that does not depend upon verifier.login.persona.org . The verifier will fetch the IdP's pubkey from the IdP, so it must be able to parse the IdP's key ("A"), as well as the browser's pubkey ("B"), the signed certificate ("C"), and the signed assertion. Both the assertion and the certificate are labeled "C", because the current jwcrypto implementation requires both to use the same format/version.
Generators
Those recipients consume messages that are generated in several places:
The IdP's pubkey "A" is served from the IdP's /.well-known/browserid file. The GET request which fetches it does not currently have a way to indicate what version of key is desired.
The browser's pubkey "B" is generated about once a day and delivered to the provisioning frame for certification.
The IdP's provisioning backend creates a signed certificate that delegates control over a specific email address to the browser's pubkey. This certificate is serialized with a specific format ("C") and returned to the browser via the provisioning frame.
The browser signs an assertion (also with format "C" to match the certificate it was given) and delivers both to the RP.
Solutions
Time-Based
The easiest approach (albeit not the one most likely to succeed) is to make time-based assumptions about which formats will be accepted by everybody. In this scheme, we tell all implementors (IdPs, browsers, and RPs) to either use our hosted services (for which we take the responsibility of keeping up-to-date), or that they'll have some window (maybe 6 months) to get their own code up-to-date. For example, if we define our current formats as "V1", then the rollout timeline for "V2" would look like:
- Jan-2013: we publish the specifications for "V2", with sample implementations. We begin modifying the hosted services to accept both V1 and V2, but only emit V1. We tell all implementors to start working on V2 tolerance.
- June-2013: we expect that all implementations are now V1+V2-tolerant
- July-2013: we modify hosted services to unconditionally emit V2. We tell all other implementors to change their code to emit V2.
- Dec-2013: we expect that no implementations are emitting V1 messages
- Jan-2014: it is now safe for implementations to remove V1 tolerance
Naturally development of V3 can proceed in parallel with the shutdown of the V1 code, so July-2013 would also see the start of V3 work, and V3 could be switched on in Jan-2014.
Pros: fairly simple, no additional messages need to be passed. Implementations only need to tolerate 2 simultaneous versions.
Cons: laggards will probably cause breakage, and the true source of the problem may be hard to track down.
Recipient-Advertisements
A more robust (but more difficult) approach is to somehow allow the creator of each message be aware of the abilities of all potential recipients.
For example, the RP might advertise which versions they can tolerate via an extra argument to the navigator.id.request() call. If the RP does not signal tolerance of the V2 format, then the browser (in Step 2) must create a V1-format assertion. It needs a V1-format certificate to do that, which may require going back to the IdP for a new (old-format) cert. It may also require creating a V1-format pubkey B. Finally, it requires the IdP to publish a V1-format pubkey A (in addition to the V2-format one it publishes for newer RPs).
Similarly, if the browser is not yet V2-capable, it needs the IdP's certificate (generated in Step 1) to be V1-format. It would most likely signal this by handing a V1-format pubkey B to the provisioning frame, and the IdP's backend code would stick to using V1-format certificates. (this is not actually sufficient for the general case, since the pubkey format can evolve independently from the certificate format).
Backing all the way up to the IdP's /.well-known/browserid advertisement, it might be useful for the browser and/or RPs to fetch different URLs from the IdP depending upon what versions they are capable of handling, or using some Accept: header negotiation to ask for different versions. Some changes won't require this (e.g. if clients will ignore any keys that they can't parse, then the IdP could publish both new- and old- format keys, and know that old clients will safely ignore the new ones), but others (like publishing multiple keys at all, rather than a single key) may need to be hidden from older clients altogether.