Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

[DSP0274](https://www.dmtf.org/dsp/DSP0274) Security Protocol and Data Model (SPDM) Specification (version [1.0.2](https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.0.2.pdf), version [1.1.4](https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.1.4.pdf), version [1.2.3](https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.2.3.pdf), version [1.3.2](https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.3.2.pdf), and version [1.4.0](https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.4.0.pdf))

[DSP0277](https://www.dmtf.org/dsp/DSP0277) Secured Messages using SPDM Specification (version [1.0.1](https://www.dmtf.org/sites/default/files/standards/documents/DSP0277_1.0.1.pdf), version [1.1.1](https://www.dmtf.org/sites/default/files/standards/documents/DSP0277_1.1.1.pdf), version [1.2.0](https://www.dmtf.org/sites/default/files/standards/documents/DSP0277_1.2.0.pdf))
[DSP0277](https://www.dmtf.org/dsp/DSP0277) Secured Messages using SPDM Specification (version [1.0.1](https://www.dmtf.org/sites/default/files/standards/documents/DSP0277_1.0.1.pdf), version [1.1.1](https://www.dmtf.org/sites/default/files/standards/documents/DSP0277_1.1.1.pdf), version [1.2.0](https://www.dmtf.org/sites/default/files/standards/documents/DSP0277_1.2.0.pdf), and version [1.3.0](https://www.dmtf.org/sites/default/files/standards/documents/DSP0277_1.3.0.pdf))

MCTP and secured MCTP follow :

Expand Down
70 changes: 70 additions & 0 deletions doc/aead_limit.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,73 @@ accordingly.

If `KEY_UPDATE` is not sent before the maximum sequence number is reached, the SPDM session will be
terminated.

## Negotiated AEAD limit (DSP0277 1.3)

[DSP0277](https://www.dmtf.org/dsp/DSP0277) version 1.3 defines a Secured Message opaque data
element `AEADlimitOE` (`SMDataID` = 2) that carries a 1-byte `AeadLimitExponent`. The AEAD limit is
`2 ^ AeadLimitExponent` messages, and `AeadLimitExponent` shall not exceed 64. When the element is
absent, the default exponent is 64 (i.e. the full 64-bit sequence number space).

This element is exchanged in the opaque data of the Session-Secrets-Exchange request and response
(`KEY_EXCHANGE` / `KEY_EXCHANGE_RSP` and `PSK_EXCHANGE` / `PSK_EXCHANGE_RSP`), and both the Requester
and the Responder populate it.

Opaque data elements are not ordered, so `AEADlimitOE` is parsed regardless of where it appears
relative to the version-selection / supported-version elements. However, the element is only defined
for secured message version 1.3 and later, so it is tied to the negotiated secured message version:

* The Requester advertises `AEADlimitOE` whenever its local secured message version list includes
1.3 (it does not yet know which version the Responder will select).
* The Responder advertises `AEADlimitOE` only when it selects secured message version 1.3 or later.
* When the negotiated secured message version is older than 1.3, a received `AEADlimitOE` is ignored
(treated as absent, default exponent 64) even if the peer included it.

### Single source of truth

There is exactly one Integrator-settable knob for the AEAD limit:
`LIBSPDM_DATA_MAX_SPDM_SESSION_SEQUENCE_NUMBER` (set with `LIBSPDM_DATA_LOCATION_LOCAL`). There is no
separate "exponent" setting; the value advertised on the wire is *derived* from this cap, so the two
can never disagree.

Because the session sequence number is checked with
`sequence_number >= max_spdm_session_sequence_number`, a cap of `2 ^ e` messages is stored as
`(2 ^ e) - 1`. The `AeadLimitExponent` this endpoint advertises is therefore the inverse:

```
AeadLimitExponent = floor(log2(max_spdm_session_sequence_number + 1))
```

* The default cap `0xFFFFFFFFFFFFFFFF` maps to exponent 64 (the spec default), and avoids computing
`2 ^ 64` (which does not fit in a 64-bit value).
* A power-of-two-minus-one cap (e.g. `0xFFFFFFFF` -> 32, `0xFFFFFF` -> 24) maps to its exact
exponent.
* A cap that is not of the form `2 ^ e - 1` rounds the advertised exponent *down* to the nearest
representable AEAD limit, i.e. the advertised limit is always `<=` the configured cap (the safe
direction).

### Establishment and enforcement

Once the session is established, each endpoint sets the session's effective maximum sequence number
to the minimum of:

* its own configured cap (`LIBSPDM_DATA_MAX_SPDM_SESSION_SEQUENCE_NUMBER`), and
* the peer's advertised AEAD limit of `2 ^ peer_AeadLimitExponent` messages (stored as
`(2 ^ peer_AeadLimitExponent) - 1`).

So neither side's limit is ever raised by the other. When the session sequence number reaches this
effective maximum, the session is terminated unless a `KEY_UPDATE` (which resets the sequence number)
is performed first.

### Reading the negotiated value

The Integrator can read the per-session effective maximum sequence number by calling
`libspdm_get_data` with `LIBSPDM_DATA_MAX_SPDM_SESSION_SEQUENCE_NUMBER` and
`LIBSPDM_DATA_LOCATION_SESSION` (with the session ID in `additional_data`). The same data ID with
`LIBSPDM_DATA_LOCATION_LOCAL` returns the global Integrator-configured cap. The advertised exponent,
if needed, is `floor(log2(value + 1))`.

The per-session maximum sequence number is read-only. It is derived once, at session establishment,
from `min(configured cap, peer AEAD limit)` and is owned by the negotiation result. `libspdm_set_data`
with `LIBSPDM_DATA_MAX_SPDM_SESSION_SEQUENCE_NUMBER` and `LIBSPDM_DATA_LOCATION_SESSION` is rejected;
only the context-level (`LIBSPDM_DATA_LOCATION_LOCAL`) cap is settable.
21 changes: 18 additions & 3 deletions include/industry_standard/spdm_secured_message.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Copyright Notice:
* Copyright 2021-2024 DMTF. All rights reserved.
* Copyright 2021-2026 DMTF. All rights reserved.
* License: BSD 3-Clause License. For full text see link: https://github.com/DMTF/libspdm/blob/main/LICENSE.md
**/

Expand Down Expand Up @@ -51,12 +51,13 @@
* AssociatedData AeadTag
*/

/* 3 means SPDM secured message version 1.0, 1.1, 1.2 */
#define SECURED_SPDM_MAX_VERSION_COUNT 3
/* 4 means SPDM secured message version 1.0, 1.1, 1.2, 1.3 */
#define SECURED_SPDM_MAX_VERSION_COUNT 4
/* the DSP0277 specification version */
#define SECURED_SPDM_VERSION_10 0x10
#define SECURED_SPDM_VERSION_11 0x11
#define SECURED_SPDM_VERSION_12 0x12
#define SECURED_SPDM_VERSION_13 0x13

typedef struct {
uint32_t session_id;
Expand Down Expand Up @@ -128,6 +129,20 @@ typedef struct {
/*spdm_version_number_t versions_list[version_count];*/
} secured_message_opaque_element_supported_version_t;

/* DSP0277 1.3 AEAD limit data (Table 10). Added in secured message version 1.3. */
#define SECURED_MESSAGE_OPAQUE_ELEMENT_SMDATA_ID_AEAD_LIMIT 0x2

/* AeadLimitExponent shall be <= 64; AeadLimit = 2 ^ AeadLimitExponent.
* When the AEAD limit data is absent, the exponent is interpreted as 64. */
#define SECURED_MESSAGE_AEAD_LIMIT_EXPONENT_MAX 64
#define SECURED_MESSAGE_AEAD_LIMIT_EXPONENT_DEFAULT 64

typedef struct {
uint8_t sm_data_version; /* SECURED_MESSAGE_OPAQUE_ELEMENT_SMDATA_DATA_VERSION*/
uint8_t sm_data_id; /* SECURED_MESSAGE_OPAQUE_ELEMENT_SMDATA_ID_AEAD_LIMIT*/
uint8_t aead_limit_exponent; /* <= 64; AeadLimit = 2 ^ aead_limit_exponent*/
} secured_message_opaque_element_aead_limit_t;

#pragma pack()

#endif /* SPDM_SECURED_MESSAGE_H */
88 changes: 88 additions & 0 deletions include/internal/libspdm_common_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,94 @@ size_t libspdm_get_opaque_data_supported_version_data_size(libspdm_context_t *sp
**/
size_t libspdm_get_opaque_data_version_selection_data_size(const libspdm_context_t *spdm_context);

/**
* Return the highest secured message version this endpoint offers in its local supported list, in
* version-number form (e.g. SECURED_SPDM_VERSION_13 << SPDM_VERSION_NUMBER_SHIFT_BIT), or 0 if the
* list is empty.
*
* @param spdm_context A pointer to the SPDM context.
*
* @return the highest locally-offered secured message version (version-number form).
**/
spdm_version_number_t libspdm_local_max_secured_message_version(
const libspdm_context_t *spdm_context);

/**
* Return the size in bytes of the DSP0277 1.3 AEAD limit opaque element when appended on its own,
* i.e. the size of a single secured-message opaque element carrying the AEADlimitOE data, with
* padding. This is the incremental size that the AEADlimitOE element adds to an opaque data blob
* that already has a general opaque data table header and at least one other element.
*
* The AEADlimitOE element is only defined for secured message version 1.3 and later; for an older
* secured_message_version this returns 0.
*
* @param spdm_context A pointer to the SPDM context.
* @param secured_message_version The secured message version this opaque data targets (the
* negotiated version for a response, or the highest locally-offered
* version for a request).
*
* @return the size in bytes of the AEAD limit opaque element (0 if the version is older than 1.3).
**/
size_t libspdm_get_opaque_data_aead_limit_element_size(const libspdm_context_t *spdm_context,
spdm_version_number_t
secured_message_version);

/**
* Append the DSP0277 1.3 AEAD limit opaque element (AEADlimitOE) to an opaque data blob that was
* built by libspdm_build_opaque_data_supported_version_data (requester) or
* libspdm_build_opaque_data_version_selection_data (responder). The general opaque data table
* header's total_elements is incremented and the new element is written after the existing
* element(s).
*
* The AEADlimitOE element is only defined for secured message version 1.3 and later; for an older
* secured_message_version this is a no-op (nothing is appended and *data_out_size is unchanged).
*
* @param spdm_context A pointer to the SPDM context.
* @param secured_message_version The secured message version this opaque data targets.
* @param data_out_size On input, the capacity of data_out. On output, the new total size.
* @param data_out The opaque data blob to extend in place.
**/
void libspdm_append_opaque_data_aead_limit_element(libspdm_context_t *spdm_context,
spdm_version_number_t secured_message_version,
size_t *data_out_size, void *data_out);

/**
* Parse the DSP0277 1.3 AEAD limit opaque element (AEADlimitOE) from received opaque data.
*
* The AEADlimitOE element is only defined for secured message version 1.3 and later. When the
* negotiated secured message version is older, the element is ignored (treated as absent) even if
* a peer included it, and the default exponent (64) is returned.
*
* @param spdm_context A pointer to the SPDM context.
* @param secured_message_version The negotiated secured message version (version-number form).
* @param data_in_size size in bytes of data_in.
* @param data_in The received opaque data.
* @param aead_limit_exponent On output, the peer's AeadLimitExponent. If the element is absent
* or ignored, this is set to the default (64).
*
* @retval LIBSPDM_STATUS_SUCCESS parsed successfully (or absent/ignored -> default).
* @retval LIBSPDM_STATUS_INVALID_MSG_FIELD the element is malformed or the exponent is > 64.
**/
libspdm_return_t libspdm_process_opaque_data_aead_limit(libspdm_context_t *spdm_context,
spdm_version_number_t
secured_message_version,
size_t data_in_size, const void *data_in,
uint8_t *aead_limit_exponent);

/**
* Apply the negotiated AEAD limit to a session's secured-message context, setting the maximum
* session sequence number to min(integrator-configured cap, 2 ^ effective_exponent), where
* effective_exponent = min(local exponent, peer exponent). Only takes effect when the session's
* secured message version is >= 1.3.
*
* @param spdm_context A pointer to the SPDM context.
* @param session_info The session to configure.
* @param peer_aead_limit_exponent The peer's AeadLimitExponent (64 if not advertised).
**/
void libspdm_apply_aead_limit_to_session(libspdm_context_t *spdm_context,
void *session_info,
uint8_t peer_aead_limit_exponent);

/**
* Return the SPDMversion field of the version number struct.
*
Expand Down
27 changes: 26 additions & 1 deletion library/spdm_common_lib/libspdm_com_context_data.c
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,13 @@ libspdm_return_t libspdm_set_data(void *spdm_context, libspdm_data_type_t data_t
if (data_size != sizeof(uint64_t)) {
return LIBSPDM_STATUS_INVALID_PARAMETER;
}
/* This is a context-level integrator cap only. A per-session value is not settable: a
* session's effective cap is derived once at establishment from min(this cap, negotiated
* DSP0277 1.3 AEAD limit) and must not be overridden afterwards. The per-session value is
* read-only via get_data(LIBSPDM_DATA_LOCATION_SESSION). */
if (parameter->location == LIBSPDM_DATA_LOCATION_SESSION) {
return LIBSPDM_STATUS_INVALID_PARAMETER;
}
context->max_spdm_session_sequence_number = *(const uint64_t *)data;
if (context->max_spdm_session_sequence_number == 0) {
context->max_spdm_session_sequence_number = LIBSPDM_MAX_SPDM_SESSION_SEQUENCE_NUMBER;
Expand Down Expand Up @@ -1122,7 +1129,21 @@ libspdm_return_t libspdm_get_data(void *spdm_context, libspdm_data_type_t data_t
break;
case LIBSPDM_DATA_MAX_SPDM_SESSION_SEQUENCE_NUMBER:
target_data_size = sizeof(uint64_t);
target_data = &context->max_spdm_session_sequence_number;
/* When a session is addressed (LIBSPDM_DATA_LOCATION_SESSION), return that session's
* effective maximum sequence number, i.e. the value negotiated via the DSP0277 1.3 AEAD
* limit (min of the integrator-configured cap and the negotiated AEAD limit). Otherwise
* return the context-level integrator-configured value. */
if (parameter->location == LIBSPDM_DATA_LOCATION_SESSION) {
session_id = libspdm_read_uint32(parameter->additional_data);
session_info = libspdm_get_session_info_via_session_id(context, session_id);
if (session_info == NULL) {
return LIBSPDM_STATUS_INVALID_PARAMETER;
}
secured_context = session_info->secured_message_context;
target_data = &secured_context->max_spdm_session_sequence_number;
} else {
target_data = &context->max_spdm_session_sequence_number;
}
break;
case LIBSPDM_DATA_VCA_CACHE:
target_data_size = context->transcript.message_a.buffer_size;
Expand Down Expand Up @@ -2871,10 +2892,14 @@ libspdm_return_t libspdm_init_context_with_secured_context(void *spdm_context,
SECURED_SPDM_VERSION_11 << SPDM_VERSION_NUMBER_SHIFT_BIT;
context->local_context.secured_message_version.secured_message_version[2] =
SECURED_SPDM_VERSION_12 << SPDM_VERSION_NUMBER_SHIFT_BIT;
context->local_context.secured_message_version.secured_message_version[3] =
SECURED_SPDM_VERSION_13 << SPDM_VERSION_NUMBER_SHIFT_BIT;
context->local_context.capability.st1 = SPDM_ST1_VALUE_US;

context->mut_auth_cert_chain_buffer_size = 0;

/* DSP0277 1.3 AEAD limit: max_spdm_session_sequence_number is the single source of truth for
* the AEAD limit. The default 0xFFFFFFFFFFFFFFFF encodes the spec-default exponent of 64. */
context->max_spdm_session_sequence_number = LIBSPDM_MAX_SPDM_SESSION_SEQUENCE_NUMBER;

context->latest_session_id = INVALID_SESSION_ID;
Expand Down
Loading
Loading