| # Client Update Protocol (CUP) |
| |
| CUP is an HTTP protocol extension used to secure communication with CUP-capable |
| servers when TLS is untrustworthy or impractical for other reasons. |
| |
| CUP provides: |
| * Integrity of the client's request body. |
| * Integrity of the server's response body. |
| * Freshness of the server's response body. |
| |
| CUP does not provide: |
| * Integrity of the client's request headers or request line. |
| * Integrity of the server's response headers. |
| * Confidentiality of the client's request. |
| * Confidentiality of the server's response. |
| * Freshness of the client's request. |
| |
| CUP requires: |
| * Control of both the client and server. |
| |
| Current CUP communications are secured by an ECDSA key pair. The client has the |
| public key hardcoded into the application binary. The server signs |
| request-response pairs with the private key, and the client verifies using the |
| public key. |
| |
| In Chromium, CUP keys are rotated annually. Each key pair has a corresponding |
| version number. To interoperate with old clients, the server must keep all |
| historical versions of the key, and be prepared to sign with any of them. The |
| client should only keep a single version (whichever was most recent at compile |
| time). |
| |
| ## Details |
| |
| ### Description |
| The server publishes an elliptic curve field/equation and a public key curve |
| point to be used by the client. In practice, these values are hardcoded into |
| each client at compile time. |
| |
| For each request, the client assembles three components: |
| 1. The message body (the request body to be sent to the server). |
| 2. A random value to be used as a nonce for freshness (at least 256 bits). |
| 3. A code to identify the public key the client will use to verify this |
| request. The client converts the public key id and nonce to a string: the |
| public key is converted to decimal, and the nonce to any url-safe encoding. |
| |
| The client stores the request body in a buffer, in UTF-8 format; it |
| appends the keyid/nonce string to this buffer. It calculates a SHA-256 hash of |
| this combined buffer, which it stores for validation later. It sends the |
| request and the keyid/nonce string to the server. |
| |
| The server receives the request body, public key id, and nonce; it performs |
| the same appending operation, and computes the SHA-256 hash of the received data |
| buffer. |
| |
| The server attempts to find a matching ECDSA private key for the specified |
| public key id, returning an HTTP error if no such private key exists. Finally, |
| it assembles the response body. |
| |
| Before sending, the server stores the response body (also in UTF-8) in a |
| buffer. It appends the computed SHA-256 hash of the request body+keyid+nonce to |
| the buffer. It then calculates an ECDSA signature over that combined buffer, |
| using the server’s private key. It sends the ECDSA signature and the response |
| body + client hash back to the user. |
| |
| The client receives the response body, observed client hash, and ECDSA signature. |
| The client compares the observed client hash to its stored request hash. If |
| there is a mismatch, then either the request or response have been tampered with |
| and the response is rejected. |
| |
| The client concatenates the request hash to the response body, and verifies the |
| signature using its public key. If verification fails, then either the request |
| or response have been tampered with and the response is rejected. |
| |
| ### HTTP Implementation |
| The request body is a POST body. The key ID and nonce, are transmitted in a |
| query parameter appended to the requested URL, using the format `&cup2key=%d:%u` |
| where the first parameter is the key ID, the second is the freshness nonce. |
| |
| For debugging purposes, the request hash is sent to the server using a query |
| parameter appended to the requested URL, using the format `&cup2hreq=%s` where |
| %s is the lowercase hexadecimal value of the hash (in big-endian format). |
| |
| The server returns the ECDSA signature and the request hash it computed in at |
| least one of three forms: |
| 1. The `X-Cup-Server-Proof` HTTP header, with the value in the format |
| `signature:hash`. |
| 2. The `ETag` HTTP header, with the value in the format of |
| `W/"signature:hash"`. |
| 3. The `ETag` HTTP header, with the value in the format of `signature:hash`. |
| |
| If multiple forms are present in the response, clients should prefer form 1, |
| falling back to form 2 only if form 1 is not present, and falling back to form 3 |
| only if form 1 and 2 are not present. |
| |
| In practice, multiple forms allow the communication to navigate different types |
| of proxies that mutate request headers. |
| |
| In all forms, `signature` is a DER-encoded ASN.1 sequence of "R" and "S", |
| rendered in lowercase hexadecimal representation. |
| |
| In all forms, `hash` is a 256-bit value rendered in lowercase hexadecimal |
| representation (big-endian). |
| |
| ### K-Repetition |
| A grave danger in any system involving ECDSA is the danger of K repetition. |
| |
| Computing an ECDSA signature starts with selecting a random 256-bit integer, |
| called K. The combination of K and the public key are used to produce the first |
| half of the signature, called R; the values of R, K, the private key, and the |
| message digest are used to compute the other half of the signature, called S. |
| |
| Because of this process, if the same value of K is chosen for two signatures, |
| both signatures will have the same value for R. If a malicious user can acquire |
| two messages that have different bodies but identical R values, a |
| straightforward computation yields the server's private key. |
| |
| Assuming that a good PRNG is used, and properly seeded, the probability of a |
| collision is small even across a large number of signatures. However, regular |
| key rotation is still recommended: |
| 1. Key material may be disclosed due to server compromise, and organizations |
| should be prepared to remediate by performing a key rotation. Regularly |
| exercising a key rotation process is important preparation. |
| 2. PRNGs are not always perfectly secure or properly seeded. |
| |
| ## See Also |
| Previous documents describing CUP can be found at: |
| * [Original Public Design](https://github.com/google/omaha/blob/master/doc/ClientUpdateProtocol.md) |
| * [ECDSA Extension](https://github.com/google/omaha/blob/master/doc/ClientUpdateProtocolEcdsa.md) |
| |
| Chromium's implementation of CUP can be found in |
| [components/client\_update\_protocol](https://source.chromium.org/chromium/chromium/src/+/main:components/client_update_protocol/). |
| |