Skip to content

Check TCB version to ensure it is up to date enough #6837

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

Merged
merged 60 commits into from
Mar 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
af6c511
Parse cpuid bytes of attestation
cjen1-msft Feb 18, 2025
0f3a613
Check that the firmware version of SNP chips is correct
cjen1-msft Feb 21, 2025
3126f94
Reformat
cjen1-msft Feb 21, 2025
300371c
Add base case if nothing matches
cjen1-msft Feb 21, 2025
4e8e38b
Move validation to tables
cjen1-msft Feb 21, 2025
c3150ea
Tmp
cjen1-msft Feb 24, 2025
6ad352b
Add minimal hacky getter
cjen1-msft Feb 24, 2025
f8baa8f
fmt
cjen1-msft Feb 24, 2025
aca3e78
Trust host's tcb
cjen1-msft Feb 24, 2025
40df0d3
Use MIN_TCB_VERIF_VERSION
cjen1-msft Feb 24, 2025
46bec1a
Make build
cjen1-msft Feb 24, 2025
b8d5c83
Use hex represntation of AttestChipModel as key
cjen1-msft Feb 25, 2025
86be11e
AttestChipModel should have json representation
cjen1-msft Feb 26, 2025
848923b
0 out remainder of TcbVersion
cjen1-msft Feb 26, 2025
0be3444
Initialize members
cjen1-msft Feb 26, 2025
8f193ae
Add test for verify_tcb_against_store
cjen1-msft Feb 28, 2025
99f2916
Move get_tcb_version out of attestation_provider. FIx import of logge…
cjen1-msft Feb 28, 2025
a966abe
poke the birds
cjen1-msft Feb 28, 2025
141e365
Reformat
cjen1-msft Feb 28, 2025
2ce3ef2
Store cpuid in kv. Check attestation against it.
cjen1-msft Mar 3, 2025
8fcda80
Fix cpuid request
cjen1-msft Mar 4, 2025
1656915
fmt
cjen1-msft Mar 4, 2025
acffafe
Add governance action for tcb_versions
cjen1-msft Mar 4, 2025
fe8d19a
Add test for tcb_versions table
cjen1-msft Mar 4, 2025
7feeb7a
fmt
cjen1-msft Mar 4, 2025
558d7e0
fixup
cjen1-msft Mar 5, 2025
6f6114b
Fix formatting of cpuid
cjen1-msft Mar 5, 2025
0681ef3
TMP store hex string of cpuid
cjen1-msft Mar 6, 2025
e070410
Fix converters
cjen1-msft Mar 7, 2025
9765a90
Document MinimumTcbVersion maps
cjen1-msft Mar 10, 2025
df6e940
Virtual should not have defualt TCB versions
cjen1-msft Mar 10, 2025
ffded3c
Check new gov openapi spec
cjen1-msft Mar 10, 2025
81b593d
Format
cjen1-msft Mar 10, 2025
c7f768c
Update changelog
cjen1-msft Mar 10, 2025
6df1b6d
Fix schema issue
cjen1-msft Mar 10, 2025
f50951d
Fix code-update-test
cjen1-msft Mar 10, 2025
c8b52fa
Fix schema issues
cjen1-msft Mar 11, 2025
fb7f4c7
Add e2e test for recovery path of tcb_versions
cjen1-msft Mar 11, 2025
491d0b0
Actually check the right tables...
cjen1-msft Mar 11, 2025
a69dce9
Reformat
cjen1-msft Mar 11, 2025
aeeb85b
Standardise and enforce uppercase CPUID to match AMD documentation
cjen1-msft Mar 18, 2025
0016f4c
fixup
cjen1-msft Mar 18, 2025
0f22460
fixup
cjen1-msft Mar 18, 2025
e8ccb9f
Ensure cpuids are uppercase
cjen1-msft Mar 19, 2025
bd129f1
Add rc2 changelog message
cjen1-msft Mar 19, 2025
870e58b
Update CHANGELOG.md
cjen1-msft Mar 20, 2025
e94a5ae
Rename add_snp_tcb_version to set_snp_minimum_tcb_version, rename rem…
cjen1-msft Mar 20, 2025
609770e
Document get_snp_attestation
cjen1-msft Mar 20, 2025
b34c5f5
Remove logger include
cjen1-msft Mar 20, 2025
562cb70
Ensure cpuid is lowercase hex string
cjen1-msft Mar 20, 2025
fe7a289
Reformat
cjen1-msft Mar 20, 2025
ca4c1f9
Remove call to logger
cjen1-msft Mar 20, 2025
b11b1cf
Document cpuid
cjen1-msft Mar 20, 2025
7147a16
Add example for nodes.snp.tcb_versions
cjen1-msft Mar 20, 2025
8afb39a
Reformat
cjen1-msft Mar 20, 2025
bc3728e
Merge branch 'main' into tcb_checking
cjen1-msft Mar 20, 2025
87ce0a1
Update src/service/internal_tables_access.h
cjen1-msft Mar 21, 2025
fc79f71
Update src/service/internal_tables_access.h
cjen1-msft Mar 21, 2025
3a879c2
Update CHANGELOG.md
cjen1-msft Mar 21, 2025
71d8324
Reformat
cjen1-msft Mar 24, 2025
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
6 changes: 3 additions & 3 deletions .snpcc_canary
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
___ ___ ___ \/
(. =) Y (0 0) (x X) Y (vv)
O \ o | / |
___ ___ ___ \_/
(. =) Y (0 0) (x X) Y (___)
O \ o | / |
/-xXx--//-----x=x--/-xXx--/---x-/--->>>--/
....
3 changes: 2 additions & 1 deletion .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"configurations": [
{
"name": "Linux",
"compileCommands": "${workspaceFolder}/build/compile_commands.json"
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"includePath": ["${workspaceFolder}/include", "${workspaceFolder}/src"]
}
],
"version": 4
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- `ccf-devel` RPM to support building CCF applications (#6904)
- `ccf` to support running pre-built CCF applications (#6909)

### Added

- Attestations of new SNP nodes must be from a trusted TCB version higher than the minimum TCB version stored for that CPU model in `public:ccf.gov.nodes.snp.tcb_versions`. Added `set_snp_minimum_tcb_version(cpuid, tcb_version)` and `remove_snp_minimum_tcb_version(cpuid)` governance actions. New networks will automatically populate the TCB version, pre-existing networks must set a TCB version when upgrading. (#6837)
- Expose `AttestationProvider::get_snp_attestation` to extract snp attestations from a quote. (#6837)

## [6.0.0-rc1]

[6.0.0-rc1]: https://github.com/microsoft/CCF/releases/tag/6.0.0-rc1
Expand Down
20 changes: 20 additions & 0 deletions doc/audit/builtin_maps.rst
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,26 @@ For Confidential Azure Container Instance (ACI) deployments, trusted endorsement

**Value** Map of issuer feed to Security Version Number (SVN) represented as JSON. See https://ietf-wg-scitt.github.io/draft-ietf-scitt-architecture/draft-ietf-scitt-architecture.html#name-issuer-identity.

``nodes.snp.tcb_versions``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The minimum trusted TCB version for new nodes allowed to join the network (:doc`SNP <../operations/platforms/snp>` only).

.. note:: For improved serviceability on confidential ACI deployments, see :ref:`audit/builtin_maps:``nodes.snp.tcb_versions``` map.

**Key** AMD CPUID, represented as a lowercase hex string without an '0x' prefix.

**Value** The minimum TCB version for that CPUID.

**Example**
.. list-table::
:header-rows: 1

* - CPUID
- TCB Version
* - ``00a00f11``
- ``{boot_loader: 4, tee: 0, snp: 24, microcode: 219}``

``service.info``
~~~~~~~~~~~~~~~~

Expand Down
1 change: 1 addition & 0 deletions doc/operations/platforms/snp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ The following governance proposals can be issued to add/remove these trusted val
- ``add_snp_host_data``/``remove_snp_host_data``: To add/remove a trusted security policy, e.g. when adding a new trusted container image as part of the code upgrade procedure.
- ``add_snp_uvm_endorsement``/``add_snp_uvm_endorsement``: To add remove a trusted UVM endorsement (Azure deployment only).
- ``add_snp_measurement``/``remove_snp_measurement``: To add/remove a trusted measurement.
- ``set_snp_minimum_tcb_version``/``remove_snp_minimum_tcb_version``: To add/remove a minimum trusted TCB version.

.. rubric:: Footnotes

Expand Down
41 changes: 39 additions & 2 deletions doc/schemas/gov/2024-07-01/gov.json
Original file line number Diff line number Diff line change
Expand Up @@ -2260,12 +2260,20 @@
"additionalProperties": {
"$ref": "#/definitions/ServiceState.UvmEndorsementFeeds"
}
},
"tcbVersions": {
"type": "object",
"description": "Collection of minimum TCB versions. Keyed by corresponding CPUID.",
"additionalProperties": {
"$ref": "#/definitions/ServiceState.MinimumTcbVersion"
}
}
},
"required": [
"measurements",
"hostData",
"uvmEndorsements"
"uvmEndorsements",
"tcbVersions"
]
},
"ServiceState.SnpQuoteInfo": {
Expand Down Expand Up @@ -2301,6 +2309,35 @@
"$ref": "#/definitions/ServiceState.FeedInfo"
}
},
"ServiceState.TcbVersion": {
"type": "object",
"description": "Minimum TCB version for a specific CPUID.",
"properties": {
"boot_loader": {
"type": "integer",
"description": ""
},
"tee": {
"type": "integer",
"description": ""
},
"snp": {
"type": "integer",
"description": ""
},
"microcode": {
"type": "integer",
"description": ""
}
}
},
"ServiceState.MinimumTcbVersion": {
"type": "object",
"description": "Collection of minimum TCB versions. Keyed by corresponding CPUID.",
"additionalProperties": {
"$ref": "#/definitions/ServiceState.TcbVersion"
}
},
"ServiceState.caCertBundle": {
"type": "string",
"description": "Chain of endorsed certificates (PEM format) leading to a CA."
Expand Down Expand Up @@ -2454,4 +2491,4 @@
"x-ms-parameter-location": "method"
}
}
}
}
61 changes: 60 additions & 1 deletion doc/schemas/gov_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1172,6 +1172,29 @@
],
"type": "object"
},
"TcbVersion": {
"properties": {
"boot_loader": {
"$ref": "#/components/schemas/uint8"
},
"microcode": {
"$ref": "#/components/schemas/uint8"
},
"snp": {
"$ref": "#/components/schemas/uint8"
},
"tee": {
"$ref": "#/components/schemas/uint8"
}
},
"required": [
"boot_loader",
"tee",
"snp",
"microcode"
],
"type": "object"
},
"TransactionId": {
"pattern": "^[0-9]+\\.[0-9]+$",
"type": "string"
Expand Down Expand Up @@ -1284,6 +1307,12 @@
},
"type": "object"
},
"string_to_TcbVersion": {
"additionalProperties": {
"$ref": "#/components/schemas/TcbVersion"
},
"type": "object"
},
"string_to_UVMEndorsementsData": {
"additionalProperties": {
"$ref": "#/components/schemas/UVMEndorsementsData"
Expand Down Expand Up @@ -1312,6 +1341,11 @@
"maximum": 18446744073709551615,
"minimum": 0,
"type": "integer"
},
"uint8": {
"maximum": 255,
"minimum": 0,
"type": "integer"
}
},
"securitySchemes": {
Expand Down Expand Up @@ -1339,7 +1373,7 @@
"info": {
"description": "This API is used to submit and query proposals which affect CCF's public governance tables.",
"title": "CCF Governance API",
"version": "4.6.1"
"version": "4.7.2"
},
"openapi": "3.0.0",
"paths": {
Expand Down Expand Up @@ -2136,6 +2170,31 @@
}
}
},
"/gov/kv/nodes/snp/tcb_versions": {
"get": {
"deprecated": true,
"operationId": "GetGovKvNodesSnpTcbVersions",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/string_to_TcbVersion"
}
}
},
"description": "Default response description"
},
"default": {
"$ref": "#/components/responses/default"
}
},
"summary": "This route is auto-generated from the KV schema.",
"x-ccf-forwarding": {
"$ref": "#/components/x-ccf-forwarding/sometimes"
}
}
},
"/gov/kv/nodes/snp/uvm_endorsements": {
"get": {
"deprecated": true,
Expand Down
9 changes: 9 additions & 0 deletions include/ccf/node/quote.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "ccf/ccf_deprecated.h"
#include "ccf/ds/quote_info.h"
#include "ccf/pal/attestation_sev_snp.h"
#include "ccf/pal/measurement.h"
#include "ccf/service/tables/host_data.h"
#include "ccf/tx.h"
Expand All @@ -22,6 +23,8 @@ namespace ccf
FailedInvalidHostData,
FailedInvalidQuotedPublicKey,
FailedUVMEndorsementsNotFound,
FailedInvalidCPUID,
FailedInvalidTcbVersion
};

class AttestationProvider
Expand All @@ -35,10 +38,16 @@ namespace ccf

static std::optional<HostData> get_host_data(const QuoteInfo& quote_info);

static std::optional<pal::snp::Attestation> get_snp_attestation(
const QuoteInfo& quote_info);

static QuoteVerificationResult verify_quote_against_store(
ccf::kv::ReadOnlyTx& tx,
const QuoteInfo& quote_info,
const std::vector<uint8_t>& expected_node_public_key_der,
pal::PlatformAttestationMeasurement& measurement);
};
QuoteVerificationResult verify_tcb_version_against_store(
ccf::kv::ReadOnlyTx& tx, const QuoteInfo& quote_info);

}
99 changes: 98 additions & 1 deletion include/ccf/pal/attestation_sev_snp.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ QPHfbkH0CyPfhl1jWhJFZasCAwEAAQ==
static_assert(
sizeof(TcbVersion) == sizeof(uint64_t),
"Can't cast TcbVersion to uint64_t");
DECLARE_JSON_TYPE(TcbVersion);
DECLARE_JSON_REQUIRED_FIELDS(TcbVersion, boot_loader, tee, snp, microcode);

#pragma pack(push, 1)
struct Signature
Expand Down Expand Up @@ -141,7 +143,10 @@ QPHfbkH0CyPfhl1jWhJFZasCAwEAAQ==
uint8_t report_id[32]; /* 0x140 */
uint8_t report_id_ma[32]; /* 0x160 */
struct TcbVersion reported_tcb; /* 0x180 */
uint8_t reserved1[24]; /* 0x188 */
uint8_t cpuid_fam_id; /* 0x188*/
uint8_t cpuid_mod_id; /* 0x189 */
uint8_t cpuid_step; /* 0x18A */
uint8_t reserved1[21]; /* 0x18B */
uint8_t chip_id[64]; /* 0x1A0 */
struct TcbVersion committed_tcb; /* 0x1E0 */
uint8_t current_minor; /* 0x1E8 */
Expand Down Expand Up @@ -261,4 +266,96 @@ QPHfbkH0CyPfhl1jWhJFZasCAwEAAQ==

virtual ~AttestationInterface() = default;
};

static uint8_t MIN_TCB_VERIF_VERSION = 3;
#pragma pack(push, 1)
// AMD CPUID specification. Chapter 2 Fn0000_0001_EAX
// Milan: 0x00A00F11
// Genoa: 0X00A10F11
// Note: The CPUID is little-endian so the hex_string is reversed
struct CPUID
{
uint8_t stepping : 4;
uint8_t base_model : 4;
uint8_t base_family : 4;
uint8_t reserved : 4;
uint8_t extended_model : 4;
uint8_t extended_family : 8;
uint8_t reserved2 : 4;

bool operator==(const CPUID&) const = default;
std::string hex_str() const
{
CPUID buf = *this;
auto buf_ptr = reinterpret_cast<uint8_t*>(&buf);
const std::span<const uint8_t> tcb_bytes{
buf_ptr, buf_ptr + sizeof(CPUID)};
return fmt::format(
"{:02x}", fmt::join(tcb_bytes.rbegin(), tcb_bytes.rend(), ""));
}
inline uint8_t get_family_id() const
{
return this->base_family + this->extended_family;
}
inline uint8_t get_model_id() const
{
return (this->extended_model << 4) | this->base_model;
}
};
#pragma pack(pop)
DECLARE_JSON_TYPE(CPUID);
DECLARE_JSON_REQUIRED_FIELDS(
CPUID, stepping, base_model, base_family, extended_model, extended_family);
static_assert(
sizeof(CPUID) == sizeof(uint32_t), "Can't cast CPUID to uint32_t");
static CPUID cpuid_from_hex(const std::string& hex_str)
{
CPUID ret;
auto buf_ptr = reinterpret_cast<uint8_t*>(&ret);
ccf::ds::from_hex(hex_str, buf_ptr, buf_ptr + sizeof(CPUID));
std::reverse(
buf_ptr, buf_ptr + sizeof(CPUID)); // fix little endianness of AMD
return ret;
}

// On SEVSNP cpuid cannot be trusted and must be validated against an
// attestation.
static CPUID get_cpuid_untrusted()
{
uint32_t ieax = 1;
uint64_t iebx = 0;
uint64_t iecx = 0;
uint64_t iedx = 0;
uint32_t oeax = 0;
uint64_t oebx = 0;
uint64_t oecx = 0;
uint64_t oedx = 0;
// pass in e{b,c,d}x to prevent cpuid from blatting other registers
asm volatile("cpuid"
: "=a"(oeax), "=b"(oebx), "=c"(oecx), "=d"(oedx)
: "a"(ieax), "b"(iebx), "c"(iecx), "d"(iedx));
auto cpuid = *reinterpret_cast<CPUID*>(&oeax);
return cpuid;
}
}

namespace ccf::kv::serialisers
{
// Use hex string to ensure uniformity between the endpoint perspective and
// the kv's key
template <>
struct BlitSerialiser<ccf::pal::snp::CPUID>
{
static SerialisedEntry to_serialised(const ccf::pal::snp::CPUID& chip)
{
auto hex_str = chip.hex_str();
return SerialisedEntry(hex_str.begin(), hex_str.end());
}

static ccf::pal::snp::CPUID from_serialised(const SerialisedEntry& data)
{
return ccf::pal::snp::cpuid_from_hex(
std::string(data.data(), data.end()));
}
};
}
9 changes: 5 additions & 4 deletions include/ccf/pal/snp_ioctl5.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,11 @@ namespace ccf::pal::snp::ioctl5
int rc = ioctl(fd, SEV_SNP_GUEST_MSG_REPORT, &payload);
if (rc < 0)
{
CCF_APP_FAIL("IOCTL call failed: {}", strerror(errno));
CCF_APP_FAIL("Payload error: {}", payload.error);
throw std::logic_error(
"Failed to issue ioctl SEV_SNP_GUEST_MSG_REPORT");
const auto msg = fmt::format(
"Failed to issue ioctl SEV_SNP: {} payload error: {}",
strerror(errno),
payload.error);
throw std::logic_error(msg);
}
}

Expand Down
Loading