-
Notifications
You must be signed in to change notification settings - Fork 30
Formally propose a transport-agnostic Cerberus #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
* Name: Transport_Agnosticism | ||
* Date: 2021-04-02 | ||
* Pull Request: [#16](https://github.com/opencomputeproject/Security/pull/16) | ||
|
||
# Objective | ||
|
||
Cerberus currently mandates that its messages be exchanged over an MCTP bus, | ||
using a Microsoft-specific PCI vendor ID. This is a limitation for deployments | ||
that might want to speak Cerberus over a completely different bus, such as | ||
SPI, I3C, exotic buses like NVME, or over plain old TCP or UDP for the | ||
purposes of conformance testing. | ||
|
||
This RFC describes a "transport agnostic" model for Cerberus with the following | ||
goals: | ||
- The existing MCTP binding of the protocol works with minimal (or no) changes. | ||
- Cerberus can be used over an arbitrary transport layer without directly | ||
implementing special support for it in a generic Cerberus library. | ||
- References to MCTP in the Cerberus Challenge Protocol specification will be | ||
moved to an appendix, describing it as a possible option for transporting | ||
Cerberus. | ||
|
||
# Proposal | ||
|
||
"Transport agnostic" Cerberus specifies an *admissible transport* as any protocol | ||
or bus that satisfies a set of properties. These properties reflect the | ||
properties of MCTP already used by Cerberus. | ||
|
||
1. An admissible transport is a mechanism for sending a *message* (a dynamically-\ | ||
sized buffer of bytes) from one (not necessarily addressable) endpoint to | ||
another. In other words, the admissible transport is responsible for removing | ||
frames from packets and assembling them in sequence. | ||
|
||
2. Each endpoint has a known *maximum message length*, measured in bytes. The | ||
transport is responsible for negotiating this parameter for each device a | ||
Cerberus device intends to speak to. This parameter must be made available | ||
to the Cerberus protocol but Cerberus itself does not negotiate it. This | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would we also stick with the currently defined maximum of 4096 bytes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could. I'm not sure there's an immediate thing this restriction buys us (it certainly doesn't benefit the client in any way I can see...). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe things change when you move to non-I2C/MTCP busses, but having a hard cap on the maximum message size is useful, especially when talking about hierarchical attestation. The PA-RoT knows if it allocates a message buffer of 4096 bytes, it will be able to fully communicate with any device. It also helps buffer sizing in the BMC to support routing messages to/from different devices within the system (e.g. PA-RoT <-> AC-RoT). |
||
parameter must be at least 64 bytes. | ||
|
||
3. Each message is either the request or response half of a Cerberus command, as | ||
defined in the Challenge Protocol. To uniquely identify the type of an | ||
incoming message, the binding of Cerberus to the admissible transport must | ||
specify a *transport-specific header* that contains at a minimum the following | ||
parameters. How they are encoded is irrelevant to Cerberus, beyond that they | ||
be computable without receiving the entire incoming message: | ||
- The command type byte (as specified in the Challenge Protocol). | ||
- Whether this message is the request or response half of the command. | ||
- Whether the payload is authenticated, i.e. whether the payload carried | ||
wether the payload carried a MAC or was encrypted using a shared secret. | ||
- The length of the incoming message, in bytes. | ||
- Addressing information that determines which device sent the message | ||
(the actual contents of this information is irrelevant to Cerberus). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are a couple of other pieces of information in the current MCTP-based header:
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if the bit about Rq is just a detail of the transport... ultimately, Cerberus doesn't actually care about those other messages, so from its perspective they would never even show up; the transport implementation would route them to the correct firmware component on its own. LMK if I'm missing something obvious. Also, I've generalized the "is encrypted" bit to "is authenticated", since that's the main value of the bit. I'm mostly hand-waving the transport security away for now until I can figure out how to incorporate it in a future RFC. For now, the transport should be imagined as "knowing" the relevant shared secret (and ancillary information, such as an IV) for decrypting and replying to a message within a session. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I certainly see the point about the Rq bit. I'm not sure about abstracting the transport security, though. I think we'd have to do it in a way that leaves open support for the Cerberus protocol session establishment flow, which uses Cerberus protocol messages, so there seems to be some interdependence there. |
||
|
||
4. Messages are "addressed", in two respects: | ||
a. A server can take a request and reply to the original sender with a | ||
response, and a client can match up requests and their responses. | ||
b. Cerberus can use addressing information of unspecified format to | ||
construct requests to a specific device (this opaque addressing | ||
information could be present in a PCD, for example). | ||
|
||
By construction, MCTP almost fulfills the above requirements: it provides framing | ||
of a message of arbitrary size, and the PCIe Vendor command provides a location | ||
for the transport-specific header. Note that Cerberus itself performs the | ||
size negotiation at the moment, so that would be moved out of the Challenge | ||
Protocol and into the transport layer. | ||
|
||
The above list is everything Cerberus needs: Cerberus can be spoken over any | ||
admissible transport with no change to the actual bytes present in the | ||
encoded messages. | ||
|
||
# Specification Changelist | ||
|
||
This RFC seeks the following changes to the Challenge Protocol spec: | ||
|
||
- Chapter 3 (Protocol and Hierarchy) should be replaced with the list of | ||
requirements given above. We do not recommend using the above text | ||
exactly; instead, the new Chapter 3 should carefully elaborate each point, | ||
to make it possible for third parties to evaluate whether their chosen | ||
transport layer is admissible. | ||
|
||
- The current contents of Chapter 3, which describes Cerberus-over-MCTP, should | ||
be moved into an appendix, provided as an example admissible transport layer | ||
for I2C connections. The same should be done for Section 8.1, which describes | ||
a legacy protocol built on top of SMBus. | ||
|
||
- The first few sections of Chapter 6 should be modified to not reference MTCP, | ||
and instead refer to the abstract Cerberus header (request bit, type, length) | ||
derived from the transport-specific header. | ||
|
||
- The MCTP-specific errors in Section 6.5 should be removed. Failures such | ||
during message assembly should be handled at the transport layer; the | ||
transport should define an error-reporting mechanism for surfacing such errors | ||
to a client. The nature of this mechanism is not relevant to Cerberus. | ||
|
||
- Mentions of MCTP in Section 6.7 should be removed. The maximum message/packet | ||
size fields should be removed, since the transport layer should negotiate | ||
these while establishing a session, and pass the maximum message size field | ||
along to Cerberus. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A few items here around the Device Capabilities.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
- The same should be done for Section 6.13. | ||
|
||
- The same should also be done for Section 6.18. Since the response makes | ||
reference to payload sizes, we recommend that, instead of replying with | ||
multiple messages of maximum size, we add a "continue" bit to the Get Log | ||
response. When this bit is set, the client must send another request, with | ||
the offset field increased by the length of the Get Log log contents field. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can leave the same message structure and semantics while achieving the goal of removing message sizes from the Challenge Protocol layer. It could be reworded to something like this: "The amount of log information returned is determined by the actual log size or the maximum message size as determined by the protocol transport, whichever is smaller. If multiple messages are required to retrieve the full log contents, the end of log data is denoted by a response message with less than the transport-defined maximum number of bytes. A request for log data with the Offset field set to equal or greater than the log size will generate a response message of 0 bytes." What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't love this, but backwards compatibility in binary format is attractive... I want to sleep on it. |
||
|
||
- The same applies to Sections 6.20, 6.23, and 6.31. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might make sense to have a blanket statement at the start of Section 6 that includes my text from the previous context, but apply it to any command that takes an offset field and whose response could span multiple messages. I think that would address the issues in these other sections (aside from removing the 'MCTP' in some cases). |
||
# Implementation Guidance | ||
|
||
Manticore currently implements the above proposal via the following procedure: | ||
1. Receive a vtable from the library user that provides a function for blocking | ||
until a request arrives from somewhere. | ||
|
||
2. When that function returns, Manticore's machinery calls a different function | ||
in the vtable for parsing the parts of the transport-specific header it needs | ||
to select a message parser and handler (request bit and type byte). | ||
|
||
3. Manticore calls into the appropriate parser, which calls a third function in | ||
the vtable that provides a `read(2)` interface over the message payload to | ||
parse the message. | ||
|
||
4. Pass the parsed message into the appropriate request handler, which constructs | ||
a response. | ||
|
||
5. Manticore then calls a fourth function in the vtable, passing along the type | ||
of the response, that instructs the transport to prepare for a response. Note | ||
that the original request contained addressing information that the vtable | ||
tracks and re-uses for addressing the reply. | ||
|
||
6. The transport encodes a response header; Manticore's serializer calls into a | ||
`write(2)` interface to encode the response. The transport may packetize | ||
the response as its internal buffer fills up. | ||
|
||
7. Manticore calls the final, sixth function to flush the response, sending the | ||
final packet. | ||
|
||
As the author understands it, this is somewhat close to how the Microsoft | ||
implementation handles messages, although it calls the MCTP library directly | ||
rather than virtually. | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Our implementation is the following:
This Microsoft implementation seems to align with the suggested changes here, with a couple of minor modifications:
|
||
Manticore's vtable interface can he found at | ||
https://github.com/lowRISC/manticore/blob/e7a532/src/net.rs#L126. | ||
|
||
# Future Work | ||
|
||
Transport security. This proposal does not cover removing the USB-C-based | ||
transport security from the Cerberus protocol, but we hope to give those | ||
details a similar treatment so that Cerberus can be spoken over transports | ||
which provide their own security such as TLS, QUIC, etc. |
Uh oh!
There was an error while loading. Please reload this page.