Enclave Key Exchange Protocol (EKEP)
Background
Enclave Key Exchange Protocol (EKEP) is the protocol used by the Asylo gRPC security stack to establish secure gRPC sessions. EKEP is based on the ALTS handshake protocol. While it follows the fundamental principles of the ALTS handshake protocol, EKEP differs from the ALTS handshake protocol in the following aspects:
- EKEP always uses ephemeral Diffie-Hellman keys. The use of ephemeral keys by both participants guarantees perfect forward secrecy. Session resumption is not supported in EKEP. ALTS supports ephemeral Diffie-Hellman keys, but also supports session resumption based on previously-generated resumption tickets. Session resumption was dropped from EKEP to keep the protocol and its implementation simple. We may revisit this limitation in the future if there is a strong need to support session resumption.
- EKEP requires that participants present credentials that are cryptographically bound to a challenge provided by their peer during the handshake. This prevents attackers that steal a participant’s ephemeral credentials from spoofing that participant in the future.
- EKEP allows each participant to assert multiple identities. The key exchange succeeds only if each peer can verify all identities asserted by their peer. This feature of EKEP allows participants to enforce rich authorization policies like “A peer can only talk to me if it is running in enclave foo and has a key that is certified by CA bar.”
- EKEP adds two messages that allow the participants to negotiate which assertions will be exchanged, along with any information necessary for generating those assertions.
Handshake Ciphersuites
An EKEP ciphersuite specifies a Diffie-Hellman (DH) group and a cryptographic hash function. EKEP currently supports only one handshake ciphersuite.
enum HandshakeCipher {
UNKNOWN_HANDSHAKE_CIPHER = 0;
CURVE25519_SHA256 = 1;
}
Note that, unlike the ALTS handshake protocol, EKEP only supports ephemeral keys, and consequently there is no distinction between ephemeral and non-ephemeral ciphersuites.
Record Protocols
A record protocol specifies a ciphersuite to use after the EKEP handshake successfully completes. EKEP supports the ALTS record protocol with 128-bit AES keys in GCM mode.
enum RecordProtocol {
UNKNOWN_RECORD_PROTOCOL = 0;
ALTSRP_AES128_GCM = 1;
}
EKEP Protocol Version
Versions of the EKEP protocol are represented as strings. The first and current
version of EKEP is "EKEP v1"
.
message EkepVersion {
optional string name = 1;
}
Additional Authenticated Data
EKEP allows participants to specify additional authenticated data that is used to configure the session and is authenticated during the handshake. This data is sent in the clear, so it should not contain any secrets.
message AdditionalAuthenticatedData {
optional bytes data = 1;
}
Identity
EKEP supports the following identity types.
enum EnclaveIdentityType {
UNKNOWN_IDENTITY = 0;
NULL_IDENTITY = 1;
CODE_IDENTITY = 2;
CERT_IDENTITY = 3;
}
NULL_IDENTITY
indicates an identity that has no associated cryptographic
credentials. CODE_IDENTITY
indicates an identity associated with a code
measurement (e.g., an SGX enclave’s code identity). CERT_IDENTITY
indicates an
identity associated with a certificate that is issued by some trusted
certification authority (e.g., an X.509 certificate). These labels are just used
for classification purposes and have no impact on the protocol.
Assertion
An assertion is a cryptographically verifiable statement of an identity; an identity corresponding to a given assertion is said to be asserted by that assertion. An assertion has the following format.
message AssertionDescription {
optional EnclaveIdentityType identity_type = 1;
optional string authority_type = 2;
}
message Assertion {
optional AssertionDescription description = 1;
optional bytes assertion = 2;
}
The description
field describes the assertion, including:
- The type of enclave identity that is asserted
- The assertion authority that is associated with the assertion
An assertion authority is an entity responsible for handling generation and verification of a particular assertion type. An assertion authority consists of:
- An assertion generator, which is responsible for generating assertions
- An assertion verifier, which is responsible for verifying assertions
All assertion authorities possess a unique name. The name of an assertion
authority may refer to an enclave technology or enclave architecture associated
with the assertion, as well as any method used to produce or verify an
assertion. For example, the string "SGX Local"
is used to denote the assertion
authority that handles SGX-based assertions between enclaves on the same (local)
platform. The string "Any"
is used for assertions that are not associated with
any particular assertion authority, such as the null assertion.
The assertion
field contains the raw bytes of the assertion. The exact format
and interpretation of these bytes is up to the assertion authority that is used
to generate and verify the assertion. One possible use of this field is to
encode a serialized protocol-buffer message.
To illustrate an example of an assertion, we point readers to the
SGX local assertion,
which is an assertion of an SGX enclave’s code identity. Such an assertion can
be described by setting the description to an identity type of CODE_IDENTITY
and the "SGX Local"
assertion authority. The assertion
field itself is set
to the raw bytes of an SGX REPORT structure.
Assertion Generation and Verification
Before exchanging assertions, EKEP participants first generate and exchange ephemeral Diffie-Hellman key-pairs and unique challenges. The size of the DH public key depends on the ciphersuite in use. The challenge is a 32-byte random value that should be produced by a CSRNG.
An assertion binds an identity to two parameters – the participant’s DH public key and some additional binding data. The size of the additional binding data as well as the exact binding mechanism depend on the assertion type and the handshake ciphersuite. In the context of EKEP, the additional binding data is always a hash of the EKEP transcript up to that point. Note that the transcript used by each participant includes the precommit message (described below in CLIENT_PRECOMMIT Message, SERVER_PRECOMMIT Message) from their peer, which contains the peer’s challenge. As a result, each of a participant’s assertions effectively binds an asserted identity to the peer-provided challenge.
EKEP allows participants to assert multiple identities within a single handshake. All identities asserted by a participant must be bound to the same DH public key. A handshake succeeds only if both participants are able to successfully verify all assertions presented by their peer, and if each asserted identity is bound to both the participant’s DH public key and the handshake transcript.
For EKEP to be compatible with a particular type of assertion, the assertion
must have an associated assertion generator and assertion verifier available.
Asylo provides a
number of assertion generator and assertion verifier libraries that can be used
to generate and verify assertions for code-based identities, certificate-based
identities, and null identities. These concepts are implemented as subclasses of
the
asylo::EnclaveAssertionGenerator
and
asylo::EnclaveAssertionVerifier
interfaces.
Assertion Offers
Before exchanging assertions, EKEP participants first exchange assertion offers. An assertion offer describes an assertion that a participant is capable of producing, and has the following format:
message AssertionOffer {
optional AssertionDescription description = 1;
optional bytes additional_information = 2;
}
The description
field uniquely identifies the assertion authority capable of
handling the assertion being offered. Some assertion verifiers may require
additional information to determine whether the assertion offer is acceptable.
The additional_information
field can be used to store such information. Note
that additional_information
is just a bytes field. The interpretation of these
bytes is specific to the assertion authority that handles the offer. One
possible usage of this field is to store a serialized protocol-buffer message.
As an illustration, consider an offer for an SGX local assertion. The offer can
be described by setting the description to an identity type of CODE_IDENTITY
and the "SGX Local"
assertion authority. Note that since SGX local
attestations can only be verified by other enclaves running on the same physical
platform, participants must first exchange machine identifiers, or local
attestation domains, to determine whether local attestation is appropriate.
This information is encoded in the additional_info
field of the offer using
the following serialized protocol-buffer message:
message SgxLocalAssertionOfferAdditionalInfo {
optional string local_attestation_domain = 1;
}
This is just one example of how the additional_information
field could be used
to encode additional data for the peer. The additional_information
field
provides a general-purpose mechanism for exchanging information related to an
assertion offer. It is up to an assertion authority to determine whether to make
use of this field and how to populate it.
Assertion Requests
In addition to exchanging assertion offers in the opening message of the handshake, EKEP participants also exchange assertion requests. An assertion request describes an assertion that is accepted by a participant. A client’s assertion request does not necessarily need to be fulfilled, but a server’s assertion request must be fulfilled. An assertion request has the following format:
message AssertionRequest {
optional AssertionDescription description = 1;
optional bytes additional_information = 2;
}
As in an assertion offer, the description
field uniquely identifies the
assertion authority capable of handling the requested assertion. Some assertion
generators may require additional information to determine whether the assertion
request can be fulfilled. The additional_information
field can be used to
store such information. Again, the interpretation of this field is specific to
the assertion authority that handles the offer. One possible usage of this field
might be to store a serialized protocol-buffer message.
As an example of an assertion request, consider a request for an SGX local
assertion. The request’s description is set to indicate an identity type of
CODE_IDENTITY
and the "SGX Local"
assertion authority. Since an SGX
attestation must be targeted at a particular enclave, the participant requesting
the assertion includes a TARGETINFO value specifying themself as the target
enclave in the request. Additionally, since SGX local assertions can only be
exchanged between enclaves on the same machine, the participant also includes a
unique machine identifier, or local attestation domain, in the request. These
two values are encoded in the additional_information
field using the following
serialized protocol-buffer message:
message LocalAssertionRequestAdditionalInfo {
optional bytes target = 1;
optional bytes local_attestation_domain = 2;
}
Handshake
Message Types
An EKEP handshake uses the following message types.
enum MessageType {
UNKNOWN_MESSAGE = 0;
ABORT = 100;
CLIENT_PRECOMMIT = 101;
SERVER_PRECOMMIT = 102;
CLIENT_ID = 103;
SERVER_ID = 104;
SERVER_FINISH = 105;
CLIENT_FINISH = 106;
}
Each of these message types corresponds to a protocol buffer message that can be exchanged during an EKEP handshake.
Handshake Sequence
The goal of the EKEP handshake is to establish shared keying material for a session. EKEP achieves this through a six-message handshake between the participants.
Subsequent sections describe the formats and use of EKEP handshake messages, as well as the message-specific verification procedures in detail.
Framing
EKEP messages are framed as shown below (offsets are shown in bytes):
size
and message-type
are both 32-bit little-endian unsigned integers.
message
is a serialized protocol buffer message.
Transcript
An EKEP transcript is the concatenation of all handshake frames exchanged up to a certain point in an EKEP handshake. Handshake frames are concatenated in the order in which they arrive.
A transcript is a complete and cumulative record of the data sent during the handshake (data is appended to the transcript, but never removed). Additionally, since participants exchange nonces and ephemeral DH public keys during an EKEP handshake, the transcript for an EKEP session contains data that is unique to that session. This provides the useful property that a transcript of an EKEP handshake uniquely represents that handshake. It follows that a cryptographic hash computed over a transcript also uniquely represents that EKEP handshake.
Transcripts are used at various points during the protocol, including Assertion Generation and Verification and Deriving EKEP Secrets.
There are a total of 6 transcripts hashes in EKEP. We define them using the following notation:
- PC is the EKEP frame containing the
CLIENT_PRECOMMIT
message - PS is the EKEP frame containing the
SERVER_PRECOMMIT
message - IC is the EKEP frame containing the
CLIENT_ID
message - IS is the EKEP frame containing the
SERVER_ID
message - FC is the EKEP frame containing the
CLIENT_FINISH
message - FS is the EKEP frame containing the
SERVER_FINISH
message - H is the cryptographic hash function from a negotiated ciphersuite
The transcript hashes are defined as follows:
**T0** = H(**PC**)
**T1** = H(**PC** || **PS**)
**T2** = H(**PC** || **PS** ||
**IC**)
**T3** = H(**PC** || **PS** ||
**IC** || **IS**)
**T4** = H(**PC** || **PS** ||
**IC** || **IS** || **FS**)
**T5** = H(**PC** || **PS** ||
**IC** || **IS** || **FS** ||
**FC**)
Note that T0 and T4 are not actually used in any derivations during the protocol. They are listed here for completeness, and to assist in tracking incremental changes to the transcript.
ABORT
Message
An ABORT
message is sent by an EKEP participant when it detects an error. Once
a participant receives an ABORT
message from its peer, it must immediately
stop the handshake. An ABORT
message has the following format.
message AbortMessage {
enum ErrorCode {
UNKNOWN_ERROR_CODE = 0;
BAD_MESSAGE = 1;
DESERIALIZATION_FAILED = 2;
BAD_PROTOCOL_VERSION = 3;
BAD_HANDSHAKE_CIPHER = 4;
BAD_RECORD_PROTOCOL = 5;
BAD_AUTHENTICATOR = 6;
BAD_ASSERTION_TYPE = 7;
BAD_ASSERTION = 8;
PROTOCOL_ERROR = 9;
INTERNAL_ERROR = 10;
}
optional ErrorCode code = 1;
optional string message = 2;
}
CLIENT_PRECOMMIT
Message
CLIENT_PRECOMMIT
format
CLIENT_PRECOMMIT
is the first message in an EKEP handshake, and has the
following format.
message ClientPrecommit {
repeated EkepVersion available_ekep_versions = 1;
repeated HandshakeCipher available_cipher_suites = 2;
repeated RecordProtocol available_record_protocols = 3;
optional AdditionalAuthenticatedData options = 4;
repeated AssertionOffer client_offers = 5;
repeated AssertionRequest client_requests = 6;
optional bytes challenge = 7;
}
It has the following constraints:
available_ekep_versions
lists the client’s available EKEP versions in order of preferenceavailable_cipher_suites
lists the client’s available cipher suites in order of preferenceavailable_record_protocols
lists the client’s available record protocols in order of preferencechallenge
must contain exactly 32 bytes of random data
Server validation of CLIENT_PRECOMMIT
The server examines CLIENT_PRECOMMIT
messages for compatibility. If it detects
any of the following incompatibilities, it sends an ABORT
message with the
specified error type:
BAD_HANDSHAKE_CIPHER
: If none of the client’s available cipher suites are acceptableBAD_ASSERTION_TYPE
: If none of the client’s offered assertion types are acceptable to the server, or if none of the client’s requested assertion types can be presented by the serverPROTOCOL_ERROR
: If the client did not provide exactly 32 bytes of random dataBAD_RECORD_PROTOCOL
: If none of the client’s available record protocols are acceptableBAD_PROTOCOL_VERSION
: If none of the client’s available EKEP versions are acceptable
SERVER_PRECOMMIT
Message
SERVER_PRECOMMIT
format
If server validation of a CLIENT_PRECOMMIT
succeeds, then the server sends a
SERVER_PRECOMMIT
message, which has the following format.
message ServerPrecommit {
optional EkepVersion selected_ekep_version = 1;
optional HandshakeCipher selected_cipher_suite = 2;
optional RecordProtocol selected_record_protocol = 3;
optional AdditionalAuthenticatedData options = 4;
repeated AssertionOffer server_offers = 5;
repeated AssertionRequest server_requests = 6;
optional bytes challenge = 7;
}
It has the following constraints:
server_requests
is a non-empty subset of the assertions that were offered by the client in theCLIENT_PRECOMMIT
messageserver_offers
is a non-empty subset of the assertions that were requested by the client in theCLIENT_PRECOMMIT
messageselected_ekep_version
is available to the client. A server is expected to select the most recent EKEP version available to both the server and client, although there is no way for the client to verify this.selected_record_protocol
is a protocol fromavailable_record_protocols
presented by the client in theCLIENT_PRECOMMIT
messageselected_cipher_suite
is a cipher suite fromavailable_cipher_suites
presented by the client in theCLIENT_PRECOMMIT
messagechallenge
must contain exactly 32 bytes of random data
Client validation of SERVER_PRECOMMIT
The client examines SERVER_PRECOMMIT
messages for compatibility. If any of the
following conditions are true, then the client sends an ABORT
message with a
PROTOCOL_ERROR
code:
selected_ekep_version
is not a version offered by the clientselected_record_protocol
is not a record protocol offered by the clientselected_cipher_suite
is not a cipher suite offered by the clientserver_requests
is not a non-empty subset of the assertions that were offered by the clientserver_offers
is not a non-empty subset of the assertions that were requested by the clientchallenge
does not contain exactly 32 bytes of random data
CLIENT_ID
Message
CLIENT_ID
format
If client validation of the server’s SERVER_PRECOMMIT
succeeds, the client
prepares a CLIENT_ID
message based on parameters included in the
SERVER_PRECOMMIT
message. The client generates a verifiable assertion to
satisfy each AssertionRequest
in the SERVER_PRECOMMIT
message.
A CLIENT_ID
message has the following format.
message ClientId {
optional bytes dh_public_key = 1;
repeated Assertion assertions = 2;
}
It has the following constraints:
dh_public_key
is a valid public key for the negotiated cipher suiteassertions
is the set of assertions requested by the server in theSERVER_PRECOMMIT
message- Each assertion in
assertions
is bound todh_public_key
and the handshake transcript T1
Server validation of CLIENT_ID
After receiving a CLIENT_ID
message, the server verifies all assertions
included in that message. The server sends an ABORT
message with a
BAD_ASSERTION
error code if any of the following are true:
- One or more of the assertions in
assertions
cannot be verified - One or more of the assertions in
assertions
is not bound todh_public_key
- One or more of the assertions in
assertions
is not bound to the handshake transcript T1 assertions
is not the set of assertions requested by the server inserver_requests
of theSERVER_PRECOMMIT
message
If the CLIENT_ID
message passes validation, the server uses its own DH private
key, the client’s dh_public_key
, and the negotiated Diffie-Hellman group to
generate a shared secret according to
Deriving EKEP Secrets.
SERVER_ID
Message
SERVER_ID
format
Next, the server prepares a SERVER_ID
message based on parameters included in
the CLIENT_PRECOMMIT
message. The server generates a verifiable assertion to
satisfy each AssertionOffer
in the SERVER_PRECOMMIT
message.
A SERVER_ID
message has the following format.
message ServerId {
optional bytes dh_public_key = 1;
repeated Assertion assertions = 2;
}
It has the following constraints:
dh_public_key
is a valid public key for the negotiated cipher suiteassertions
is the set of assertions offered by the server in theSERVER_PRECOMMIT
message- Each assertion in
assertions
is bound todh_public_key
and the handshake transcript T2
Client validation of SERVER_ID
After receiving a SERVER_ID
message, the client verifies all the assertions
provided by the server. The client sends an ABORT
message with a
BAD_ASSERTION
error code if any of the following conditions hold:
- One or more of the assertions in
assertions
cannot be verified - One or more of the assertions in
assertions
is not bound todh_public_key
- One or more of the assertions in
assertions
is not bound to the handshake transcript T2 assertions
is not the set of assertions offered by the server inserver_offers
of theSERVER_PRECOMMIT
message
If the SERVER_ID
message passes validation, the client uses its own DH private
key, the server’s dh_public_key
, and the negotiated Diffie-Hellman group to
generate a shared secret according to
Deriving EKEP Secrets.
SERVER_FINISH
Message
SERVER_FINISH
format
After sending a SERVER_ID
message, the server is able to derive a Primary
Secret MS and an Authenticator Secret AS1. This
is done as described in Deriving EKEP Secrets.
The server then derives handshake_authenticator
as follows:
handshake_authenticator = HMAC-H(**AS**,
**XS**)
We use the following definitions in the above computation:
- H is the hash function from the negotiated
HandshakeCipher
- HMAC-H is the HMAC function (as defined in RFC 2104) instantiated with H
- XS is
"EKEP Handshake v1: Server Finish"
as a UTF-8 encoded, non null-terminated string, and is used as thetext
parameter. - AS is the Authenticator Secret computed by the server, and is used as the key.
After computing a handshake_authenticator
, the server sends a
SERVER_FINISH
message, which has the following format.
message ServerFinish {
optional bytes handshake_authenticator = 1;
}
Client validation of SERVER_FINISH
The client verifies that:
handshake_authenticator ≟ HMAC-H(**AC**,
**XS**)
Where XS, AC, and HMAC-H have the same definitions as before.
If the client’s HMAC value does not match, the client aborts the handshake by
sending an ABORT
message with a BAD_AUTHENTICATOR
error code.
CLIENT_FINISH
Message
CLIENT_FINISH
format
If the client verification of the SERVER_ID
and the SERVER_FINISH
messages
succeeded, then the client can derive the shared secret using the server’s DH
public key (also constant within all assertions provided by the server), its own
DH private key, and the negotiated Diffie-Hellman group. The client then derives
MC and AC as described in
Deriving EKEP Secrets.
The client then computes handshake_authenticator
as follows:
handshake_authenticator = HMAC-H(**AC**,
**XC**
)
We use the following definitions in the above computation:
- H is the hash function from the negotiated
HandshakeCipher
- HMAC-H is the HMAC function (as defined in RFC 2104) instantiated with H
- XC is
"EKEP Handshake v1: Client Finish"
as a UTF-8 encoded, non null-terminated string, and is used as thetext
parameter. - AC is the Authenticator Secret computed by the client, and is used as the key.
After computing a handshake_authenticator
, the client sends a
CLIENT_FINISH
message, which has the following format.
message ClientFinish {
optional bytes handshake_authenticator = 1;
}
Server validation of CLIENT_FINISH
The server verifies that:
handshake_authenticator ≟ HMAC-H(**AS**,
**XC**)
Where XC, AS, and HMAC-H have the same definitions as before.
If the server’s HMAC value does not match, the server silently closes the
connection without sending an ABORT
message (no handshake messages are
exchanged after a CLIENT_FINISH
message is sent).
Deriving EKEP Secrets
Based on the first four handshake messages exchanged, the client and server both derive two additional secrets: a Primary Secret M and an Authenticator Secret A.
M and A are both 512 bits in size.
We use the following definitions:
- H is the hash function from the negotiated
HandshakeCipher
- HKDF-Extract and HKDF-Expand are used as defined in RFC-5869. Both are instantiated using the hash function H.
- T3 is the value defined in Transcript
- S1 is
"EKEP Handshake v1"
as a UTF-8 encoded, non null-terminated string, and is used as thesalt
parameter in HKDF-Extract. - C is the ephemeral shared secret derived from the client and server’s DH parameters using the negotiated Diffie-Hellman group, and is the key in HKDF-Extract.
A pseudo-random key K1 is derived as follows:
**K1** = HKDF-Extract(**S1**, **C**)
M and A are derived as follows:
(**M** || **A**) = HKDF-Expand(**K1**, **T3**)),
(512 * 2) / 8)
Deriving Record Protocol Secrets
After a successful EKEP handshake, a client and server both derive a key of appropriate length for the negotiated record protocol. Note that any errors that occur during derivation of the record-protocol key are not treated as handshake protocol errors. If a participant encounters an error during key derivation, they must not send an Abort message and must instead silently close the connection.
We use the symbols defined and derived in Deriving EKEP Secrets. Additionally, we use the following new definitions:
- H, HKDF-Extract, and HKDF-Expand have the same definitions as above
- T5 is the value defined in Transcript
- S2 is
"EKEP Record Protocol v1"
as a UTF-8 encoded, non null-terminated string - L is the length of the key needed for the record protocol (in bytes)
The record protocol key X is derived through another iteration of HKDF, as follows:
**K2** = HKDF-Extract(**S2**, **M**)
**X** = HKDF-Expand(**K2**, **T5**), **L**)
Notes
-
AS and AC refer to the Authenticator Secret A derived by the server and the client respectively. In a successful EKEP handshake, AS and AC are expected to take on the same value. (Likewise for MS and MC). ↩