Skip to content

Commit

Permalink
Propose an RFC for version negotiation in Cerberus
Browse files Browse the repository at this point in the history
  • Loading branch information
mcy committed Sep 20, 2021
1 parent d9c9cc3 commit acb2497
Showing 1 changed file with 205 additions and 0 deletions.
205 changes: 205 additions & 0 deletions 0003-Version_Detection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
* Name: Version_Detection
* Date: 2021-09-20
* Pull Request: [#NNNN](https://github.com/opencomputeproject/Security/pull/NNNN)

# Objective

Cerberus does not currently expose a particularly precise interface for
discovering the version of a remote device being challenged or otherwise
interacted with. However, Cerberus does make reference to these versions as part
of the `CHALLENGE` message.

Cerberus also has significant "optional" components, though there is no way to
discover whether a remote device supports them. In theory this would be provided
to a PA-RoT via a manifest, but it some cases it may be useful to query this
information dynamically.

This RFC describes:
- A formal versioning scheme for the overall protocol, as well as its optional
subcomponents.
- A protocol command for querying this version information.
- A way for vendors to allow for unambiguous querying of their vendor
extensions.
- A story for deprecation of commands.

# Proposal

Although Cerberus has some minor prior art around version numbers, we will be
starting over from a clean slate.

## Protocol Version Numbers

A Cerberus version is a sixteen-bit opaque value, mapped to a commit hash of
this repository. Protocol version N is described by the repository at that
commit.

Versions are recorded as Git tags with the name `protocol-v{#}`, where
`{#}` is replaced with the version number. There will also be a file,
`PROTOCOL_VERSIONS.md`, which contains a list of all versions, their dates,
and a changelog, in the following format:
```
# `protocol-v{#}`
Date: YYYY-MM-DD
<changelog>
```

To create a new version:
1. Create a commit that adds the new version to `PROTOCOL_VERSIONS.md`.
2. The pull request created from that commit acts as an opportunity to discuss
the new version and changelog.
3. Once approved, the author must ensure the date on the new version matches.
4. A maintainer will merge the PR, and then create a lightweight tag with the
correct name pointing to the merged commit. This can be done via
`git tag protocol-v{#} && git push origin --tags`. This must be a push to
the upstream repository.

This proposal does not stipulate guidelines under which creating a new version
is recommended; maintainers should create new versions as they deem necessary.

After this RFC is merged and implemented, a new version, version 0, must be
created immediately.

## Subcomponents

A subcomponent is a set of optional Cerberus commands that need to be provided
together, such as the PFM-related commands. Each subcomponent has a byte
associated with it.

The subcomponents, their identifying bytes, and the list of commands in each
should be listed in a table, just past the list of all commands. All optional
commands must be part of a subcomponent, and a command may be part of multiple
components.

## The `Protocol Version` Command

We define a new command, `Protocol Version`, using command byte `0x05`, and
marked as *required*. Its purpose is to negotiate a shared version for the
devices to communicate over.

A request has the following structure:

```c
struct ProtocolVersionRequest {
uint8_t reserved; // Must be zero.

uint16_t min_version;
uint16_t max_version;

uint8_t bad_versions_len;
uint16_t bad_versions[bad_versions_len];

uint8_t extns_len;
struct {
uint8_t len;
uint8_t name[len];
} extns[extns_len];
}
```

The requester provides the minimum and maximum versions (inclusive) it
understands, as well as a list of versions it refuses to use (for security
or other reasons). It also provides a list of vendor-defined extensions it
knows how to speak.

Vendor-defined extensions are defined by strings, to
avoid running into the usual "private use area" problems in code-point
allocation. Vendors should choose strings that incorporate their name into them
to avoid chances of collision. Period-separated names are ideal:
- `widgetsinc.unsealing-with-chacha20`
- `acmeco.fancy-pcie-update-scheme`

The response looks like this:

```c
struct ProtocolVersionResponse {
uint16_t version;

uint8_t extns_len;
uint16_t extn_versions[extns_len];
};
```

This provides the responder-chosen version, and the versions of the requested
vendor-defined extensions. A version of `0xffff` is used as a sentinel to
indicate that the extension was unrecognized.

All messages that follow must use the chosen version number. This number not
only specifies which messages are supported, but also which format to use for
parsing commands, since that may vary across versions. The `Protocol Version`
command, however, is unversioned. A reserved byte is included at the top of
the command in case we ever need to change it.

A new error code, "Unnegotiated Version" (code `0x05`), should be returned by
devices if no version has been negotiated yet with the requesting device.
Requesters which had previously negotiated a version, but which recieve this
message, should re-negotiate a version.

## Updates to `CHALLENGE`

Because only a single protocol version is negotiated, we can replace the version
range in the `CHALLENGE` with the single negotiated version. This doesn't change
the layout of the `CHALLENGE` in a meaningful way, since we are replacing two
eight-bit fields with one sixteen-bit field.

## Evolution and Deprecation Process

This versioning scheme gives us a way to freely evolve the protocol without
fear of subtle incompatibility: because one version is chosen, there is no
ambiguity about different layouts of commands.

We also get deprecation for free: if we remove a message, devices can, given
sufficiently wide range of advertised versions, select a version both are
can work with. We can even re-use command bytes across versions, if that ever
becomes a problem.

There is no particular reason to mark messages as deprecated in the spec itself,
although it may be worthwhile to do so to indicate that they will eventually be
removed. Whether to leave messages deprecated for a version or two, or to remove
them immediately, is up to the maintainers.

This introduces the risk that two devices may refuse to interoperate due to
incompatible versions. It is up to implementers to chose a sufficiently large
range of versions to interoperate with other vendors' devices.

# Specification Changelist

The following changes are required:
- The creation of `PROTOCOL_VERSIONS.md` as described above.
- Prose in a section immediately before the `RoT Commands` section that
describes the Cerberus Protocol versioning scheme, including protocol
subcomponents and vendor-defined extensions.
- A table immediately after the table of all commands, which defines the
protocol subcomponents.
- Add the new error code to the error codes table.
- A new message definition, after `Device Information`, for the new `Protocol
Version` command. This shall include prose of the negotiation process.
- The min/max version fields in the `CHALLENGE` should be replaced with the
single negotiated version.

# Implementation Guidance

Implementers which support a range of Cerberus versions will need to maintain a
"currently negotiated version" for each bus they can service requests from.
This should not be a significant cost, given they already need to maintain
similar information for sessions.

Because requesters must know how to re-establish a negotiated version, an
implementer can choose to record only a single version at a time.

# Alternatives Considered

An alternative is to have requester-chosen, rather than responder-chosen,
versions. This doesn't have any specific benefits for us, but it does have the
downside that we need to deal with different versions having different layouts
for commands, meaning that the requester still needs to inform the responder
about which protocol version it wishes to use.

This could be worked around by being careful about how commands are evolved, but
it's likely to be rare enough that command formats change that the complexity
would be worth it.

# Future Work

This RFC does not describe norms and practices around when to mint versions nor
when to make the actual decision of deprecation; these are left up to the
maintainers or a potential future RFC.

0 comments on commit acb2497

Please sign in to comment.