Skip to content

Local sealing #6966

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 138 commits into from
May 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
138 commits
Select commit Hold shift + click to select a range
a608b81
Check the integrity of decrypted ciphertext
cjen1-msft Apr 7, 2025
fd8ffbf
Refactor
cjen1-msft Apr 7, 2025
5d741cf
Fmt
cjen1-msft Apr 7, 2025
79f7a0a
Add config options to startup_config for sealing
cjen1-msft Apr 7, 2025
6f29ebf
Checkpoint for unsealing ledger
cjen1-msft Apr 8, 2025
a2010bc
Disable local sealing by default
cjen1-msft Apr 9, 2025
a2f014b
revert accidental removal of previous_identity_file field
cjen1-msft Apr 9, 2025
665f04f
Add test and ensure it passes
cjen1-msft Apr 9, 2025
edc8423
Reformat
cjen1-msft Apr 9, 2025
28c1eb7
Run recovery test on 3 nodes
cjen1-msft Apr 9, 2025
f3fedeb
Horrible hacks to allow unsealing.
cjen1-msft Apr 10, 2025
ef57b9d
Fix sealing on joined nodes
cjen1-msft Apr 11, 2025
4ded7a0
Add test for rekey and refresh of recovery shares
cjen1-msft Apr 11, 2025
d79f52e
Ensure sealing key is set by platform not device
cjen1-msft Apr 11, 2025
9780bb6
Expand testing to include all rekeying paths
cjen1-msft Apr 11, 2025
56b361e
Add config changes to cchost_config.json
cjen1-msft Apr 11, 2025
79f995f
Throw error on unsealing fail. Disallow virtual unsealing
cjen1-msft Apr 11, 2025
5507b5f
Merge branch 'main' into local-sealing
cjen1-msft Apr 11, 2025
4684613
Ensure that auto-dr is gated on an attested field (CLI args)
cjen1-msft Apr 11, 2025
cef27e4
Reformat
cjen1-msft Apr 11, 2025
aed43a0
Run schema_test_cft in 1ES pools
cjen1-msft Apr 14, 2025
2f841f7
Fix typo
cjen1-msft Apr 14, 2025
0af717b
Ensure the TCB hasn't changed between sealing and unsealing
cjen1-msft Apr 14, 2025
4a2a5ca
Fmt
cjen1-msft Apr 14, 2025
87d62fb
Refactor where sealing is done to be more principled.
cjen1-msft Apr 15, 2025
59e406e
PR comments
cjen1-msft Apr 15, 2025
f614775
Roll back running e2e tests on snp c-aci
cjen1-msft Apr 15, 2025
f212366
Add test for unsealing using corrupt ledgers
cjen1-msft Apr 15, 2025
c94d852
Ensure corruption test runs
cjen1-msft Apr 15, 2025
8a808f0
Format and propogate change of sealed_ledger_secret to jinja
cjen1-msft Apr 15, 2025
5f0703c
Store tag
cjen1-msft Apr 15, 2025
5a7d455
Fixup test
cjen1-msft Apr 15, 2025
de1a6ee
MUST_REVERT: Only run the local-sealing test
cjen1-msft Apr 16, 2025
e8cf0d1
Add golden file for a sealed ledger secret from another node
cjen1-msft Apr 16, 2025
be650dd
Make sealed_ledger_secret_location driven by enable_auto_dr
cjen1-msft Apr 16, 2025
c351e6b
Fmt
cjen1-msft Apr 16, 2025
c0fde7a
Pass enable_auto_dr through to node
cjen1-msft Apr 16, 2025
d9ba872
Add logging for sealing of secrets.
cjen1-msft Apr 16, 2025
c89bca0
reformat
cjen1-msft Apr 16, 2025
d3fa6d2
corrupted sealing should not have spaces
cjen1-msft Apr 17, 2025
c404aad
Ensure the TCB hasn't changed between sealing and unsealing
cjen1-msft Apr 14, 2025
99f7c94
Add test that tcb changes are reflected in different keys.
cjen1-msft Apr 17, 2025
907acb3
Trial remove bitfields
cjen1-msft Apr 17, 2025
01b49fd
Remove bitfields from guest_field_select and account for endianness
cjen1-msft Apr 17, 2025
6010635
Use bitwise operators rather than bitfields
cjen1-msft Apr 17, 2025
377b328
Merge branch 'bitfields-eol' into local-sealing
cjen1-msft Apr 17, 2025
f33ff23
Fmt
cjen1-msft Apr 17, 2025
746e25a
Default initialise TcbVersions
cjen1-msft Apr 17, 2025
f3a1b4e
Merge branch 'bitfields-eol' into local-sealing
cjen1-msft Apr 17, 2025
82b74d8
Change sealed key
cjen1-msft Apr 17, 2025
50f2079
Log tcb version when sealing
cjen1-msft Apr 17, 2025
8f4888d
Expand test to show that decrypting with a incorrect key causes a fai…
cjen1-msft Apr 17, 2025
0cb9acb
Add unit test for junk keys
cjen1-msft Apr 22, 2025
c86e9ad
fix
cjen1-msft Apr 22, 2025
6c0efde
fix
cjen1-msft Apr 22, 2025
c25da8e
Fix bug
cjen1-msft Apr 22, 2025
a5d094e
Fmt and tidy
cjen1-msft Apr 22, 2025
8f18d1a
ReFmt
cjen1-msft Apr 22, 2025
6970dc2
Randomise IVs for sealing
cjen1-msft Apr 22, 2025
dd9025e
Tidy
cjen1-msft Apr 22, 2025
f128a8c
Update sealing file
cjen1-msft Apr 22, 2025
aa2428e
Maybe fix asan
cjen1-msft Apr 22, 2025
7513fe1
Tag e2e_ops runs
cjen1-msft Apr 22, 2025
44ef058
Reformat
cjen1-msft Apr 22, 2025
d1b0b27
Merge branch 'main' into local-sealing
cjen1-msft Apr 22, 2025
1f265c1
Revert schema test on 1ES pools
cjen1-msft Apr 22, 2025
076cf3c
Ensure e2e_tutorial is accessible
cjen1-msft Apr 22, 2025
289865d
Reorder
cjen1-msft Apr 23, 2025
12f3c61
Add .aad file
cjen1-msft Apr 23, 2025
7d1d20b
Move local sealing to its own file
cjen1-msft Apr 23, 2025
6e7c468
Maybe fix sealing
cjen1-msft Apr 23, 2025
90efd59
Actually write the thing
cjen1-msft Apr 23, 2025
922d072
Merge branch 'main' into local-sealing
cjen1-msft Apr 24, 2025
470f717
Ensure that the correct file is unsealed
cjen1-msft Apr 23, 2025
299fc74
Add schema_test_cft
cjen1-msft Apr 24, 2025
7dbc52e
Use canned aad
cjen1-msft Apr 24, 2025
abb4595
Add encoding
cjen1-msft Apr 24, 2025
de93733
fix
cjen1-msft Apr 24, 2025
c54c7fb
add pragma
cjen1-msft Apr 24, 2025
839f397
Bump timeout to 20 mins and run schema_test_cft
cjen1-msft Apr 25, 2025
3cdbe7c
Change local sealing to act on a directory
cjen1-msft Apr 25, 2025
1f47db8
Pass through versions to unsealing
cjen1-msft Apr 28, 2025
4ea7d00
Try to create sealing directory
cjen1-msft Apr 28, 2025
157a5b7
Fmt
cjen1-msft Apr 30, 2025
d6c216b
move most tests to dir version
cjen1-msft Apr 30, 2025
fec978d
Tentative fix for tests for sealing directory
cjen1-msft May 1, 2025
0a7746a
Validate that the platform is snp before allowing local unsealing
cjen1-msft May 7, 2025
ef0576e
Fix copy semantics
cjen1-msft May 7, 2025
624e6c5
Fix substring of ledger files
cjen1-msft May 7, 2025
52c20d7
fmt
cjen1-msft May 7, 2025
e2f69ec
Fix test (bar corruption)
cjen1-msft May 8, 2025
612c16b
Merge branch 'main' into local-sealing
cjen1-msft May 8, 2025
bc942ac
Reboop
cjen1-msft May 8, 2025
2b1d729
fix condition for ignoring sealed secrets
cjen1-msft May 8, 2025
bf39385
Better corruption test
cjen1-msft May 8, 2025
2433d50
fix return type of max_version corruption
cjen1-msft May 8, 2025
e0945be
Ensure corrupted secrets dir exists
cjen1-msft May 8, 2025
e846051
add src_dir
cjen1-msft May 8, 2025
cb51072
Add mkdir
cjen1-msft May 8, 2025
60a3e18
Better error messages?
cjen1-msft May 8, 2025
f7915e6
Allow corrutpions to be ignored
cjen1-msft May 8, 2025
e0ef18f
Better failure error message
cjen1-msft May 8, 2025
aadd686
Fix version check
cjen1-msft May 8, 2025
05ce786
Add test showing that invalid files are ignored.
cjen1-msft May 8, 2025
dff4b17
Reboop
cjen1-msft May 8, 2025
0092ec6
Specify error in snp_ioctl_test
cjen1-msft May 8, 2025
b52cada
Merge branch 'main' into local-sealing
cjen1-msft May 9, 2025
22870f2
Update src/node/local_sealing.h
cjen1-msft May 14, 2025
e6bf097
single json file
cjen1-msft May 14, 2025
13c39a6
Update e2e_ops
cjen1-msft May 14, 2025
c62c556
Update comment
cjen1-msft May 14, 2025
d8fe670
Fix typo
cjen1-msft May 14, 2025
abaef07
Remove cli flag and add documentation for new kv writes
cjen1-msft May 14, 2025
3ba484c
Fix
cjen1-msft May 14, 2025
ef539a9
fix
cjen1-msft May 14, 2025
e3326dc
Reformat and bump openapi
cjen1-msft May 14, 2025
1b2e5ab
bump version change
cjen1-msft May 14, 2025
32af383
Fix bug ...
cjen1-msft May 14, 2025
4edc2d6
Fix
cjen1-msft May 14, 2025
07876cf
Fix bytes
cjen1-msft May 14, 2025
5ce8e5e
fix
cjen1-msft May 14, 2025
8667e3b
Fix encoding of aad
cjen1-msft May 14, 2025
4ef5d03
Fix the b64 call
cjen1-msft May 14, 2025
97e3d76
Merge branch 'main' into local-sealing
achamayou May 14, 2025
bd883a2
fix ops encoding
cjen1-msft May 14, 2025
4f10bca
comment
cjen1-msft May 14, 2025
1666966
Reboop
cjen1-msft May 14, 2025
873af6d
remove copy_tree
cjen1-msft May 15, 2025
d6ebb19
Merge branch 'main' into local-sealing
achamayou May 15, 2025
55b8e3f
Update tests/infra/path.py
achamayou May 15, 2025
3a4d730
Update tests/schema.py
achamayou May 15, 2025
7b46682
Update .github/workflows/ci.yml
achamayou May 15, 2025
b2b37e5
Add testing of kv info and documentation.
cjen1-msft May 15, 2025
f10626d
Update doc/host_config_schema/cchost_config.json
cjen1-msft May 16, 2025
5fa8f66
Update doc/host_config_schema/cchost_config.json
cjen1-msft May 16, 2025
a5350f5
Update doc/operations/recovery.rst
cjen1-msft May 16, 2025
7a5339e
Update src/node/node_state.h
cjen1-msft May 16, 2025
ef7de58
Merge branch 'main' into local-sealing
achamayou May 16, 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.

[6.0.2]: https://github.com/microsoft/CCF/releases/tag/ccf-6.0.2

### Added

- Add support for [recovery from locally sealed secrets](https://microsoft.github.io/CCF/main/operations/recovery.html#local-sealing-recovery). (#6966)

### Changed

- SymCrypt backend is pinned to 1.7.0 until https://github.com/microsoft/SymCrypt-OpenSSL/issues/115 gets shipped (#6995).
Expand Down
7 changes: 7 additions & 0 deletions doc/audit/builtin_maps.rst
Original file line number Diff line number Diff line change
Expand Up @@ -565,3 +565,10 @@ While the contents themselves are encrypted, the table is public so as to be acc
**Key** Sentinel value 0, represented as a little-endian 64-bit unsigned integer.

**Value** Last signed Merkle root of previous service instance, represented as a hex-encoded string.

``last_recovery_type``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Value** The mechanism by which the ledger secret was recovered.

.. doxygenenum:: ccf::RecoveryType
:project: CCF
8 changes: 8 additions & 0 deletions doc/host_config_schema/cchost_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,10 @@
"previous_service_identity_file": {
"type": "string",
"description": "Path to the previous service certificate (PEM) file"
},
"previous_sealed_ledger_secret_location": {
"type": ["string"],
"description": "Path to the sealed ledger secret folder, the ledger secrets for the recovered service will be unsealed from here instead of reconstructed from recovery shares."
}
},
"required": ["previous_service_identity_file"],
Expand Down Expand Up @@ -670,6 +674,10 @@
"rpc_addresses_file": {
"type": "string",
"description": "Path to file in which all RPC addresses (hostnames and ports) will be written to on startup. This option is particularly useful when binding to port 0 and getting auto-assigned a port by the OS. No file is created if this entry is not specified"
},
"sealed_ledger_secret_location": {
"type": "string",
"description": "Path to the folder where the node will seal its ledger secrets."
}
},
"description": "This section includes configuration for additional files output by the node",
Expand Down
32 changes: 32 additions & 0 deletions doc/operations/recovery.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,38 @@ Summary Diagram

Once operators have established a recovered crash-fault tolerant public network, the existing members of the consortium :ref:`must vote to accept the recovery of the network and submit their recovery shares <governance/accept_recovery:Accepting Recovery and Submitting Shares>`.

Local Sealing Recovery
----------------------

SNP provides the `DERIVED_KEY` guest message which derives a key from the CPU's VCEK (or VLEK), TCB version and the guest's measurement and host_data (policy), thus any change to the CPU, measurement or policy, or a rolled-back TCB version, will prevent the key from being reconstructed.
If configured, the node will unseal the secrets it previously sealed instead of waiting for recovery shares from members after `transition_to_open` is triggered.

If, in config.json, `output_files.sealed_ledger_secret_location` is set, the node will derive a key and seal versioned ledger secrets to that directory.
This capability is noted in `public:ccf.gov.node.info[node].will_locally_seal_ledger_secrets`, to allow it to be audited.

Then if `command.recover.previous_sealed_ledger_secret_location` is set in the config.json, when the node recovers and receives the `transition_to_open` transaction, the node will try to unseal the latest ledger secret and use that to recover the ledger.
If this is unsuccessful, it will fall back to waiting for recovery shares.
Which of these two paths is taken is noted in the `public:ccf.internal.last_recovery_type`.

.. code-block:: bash

$ cat /path/to/config/file
...
"command": {
"type": "Recover",
...
"recover": {
...
"previous_sealed_ledger_secret_location": "/path/to/previous/secret"
}
}
"output_files": {
...
"sealed_ledger_secret_location": "/path/to/new/secret"
}
...
$ cchost --config /path/to/config/file

Notes
-----

Expand Down
5 changes: 4 additions & 1 deletion doc/schemas/gov_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,9 @@
},
"status": {
"$ref": "#/components/schemas/NodeStatus"
},
"will_locally_seal_ledger_secrets": {
"type": "boolean"
}
},
"required": [
Expand Down Expand Up @@ -1373,7 +1376,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.7.2"
"version": "4.7.3"
},
"openapi": "3.0.0",
"paths": {
Expand Down
4 changes: 4 additions & 0 deletions include/ccf/node/startup_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ namespace ccf
std::string service_subject_name = "CN=CCF Service";
ccf::COSESignaturesConfig cose_signatures;

std::optional<std::string> sealed_ledger_secret_location;

nlohmann::json service_data = nullptr;

nlohmann::json node_data = nullptr;
Expand Down Expand Up @@ -132,6 +134,8 @@ namespace ccf
{
std::optional<std::vector<uint8_t>> previous_service_identity =
std::nullopt;
std::optional<std::string> previous_sealed_ledger_secret_location =
std::nullopt;
};
Recover recover = {};
};
Expand Down
6 changes: 5 additions & 1 deletion include/ccf/service/node_info_network.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,9 @@ namespace ccf

/// ACME configuration
std::optional<ACME> acme = std::nullopt;

// Denote whether this node will locally seal the ledger secret
bool will_locally_seal_ledger_secrets = false;
};

DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(
Expand All @@ -186,7 +189,8 @@ namespace ccf
DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(NodeInfoNetwork_v2);
DECLARE_JSON_REQUIRED_FIELDS(
NodeInfoNetwork_v2, node_to_node_interface, rpc_interfaces);
DECLARE_JSON_OPTIONAL_FIELDS(NodeInfoNetwork_v2, acme);
DECLARE_JSON_OPTIONAL_FIELDS(
NodeInfoNetwork_v2, acme, will_locally_seal_ledger_secrets);

struct NodeInfoNetwork : public NodeInfoNetwork_v2
{
Expand Down
7 changes: 5 additions & 2 deletions src/common/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ namespace ccf

DECLARE_JSON_TYPE(StartupConfig::Recover);
DECLARE_JSON_REQUIRED_FIELDS(
StartupConfig::Recover, previous_service_identity);
StartupConfig::Recover,
previous_service_identity,
previous_sealed_ledger_secret_location);

DECLARE_JSON_TYPE_WITH_BASE(StartupConfig, CCFConfig);
DECLARE_JSON_REQUIRED_FIELDS(
Expand All @@ -135,5 +137,6 @@ namespace ccf
node_data,
start,
join,
recover);
recover,
sealed_ledger_secret_location);
}
11 changes: 11 additions & 0 deletions src/ds/files.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,15 @@ namespace files
ec.message()));
}
}

static void create_directory(const fs::path& dir)
{
std::error_code ec;
fs::create_directory(dir, ec);
if (ec && ec != std::errc::file_exists)
{
throw std::logic_error(fmt::format(
"Could not create directory {}: {}", dir.string(), ec.message()));
}
}
}
10 changes: 8 additions & 2 deletions src/host/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ namespace host
std::string node_to_node_address_file = "";
std::string rpc_addresses_file = "";

std::optional<std::string> sealed_ledger_secret_location = std::nullopt;

bool operator==(const OutputFiles&) const = default;
};
OutputFiles output_files = {};
Expand Down Expand Up @@ -157,6 +159,8 @@ namespace host
{
size_t initial_service_certificate_validity_days = 1;
std::string previous_service_identity_file;
std::optional<std::string> previous_sealed_ledger_secret_location =
std::nullopt;
bool operator==(const Recover&) const = default;
};
Recover recover = {};
Expand All @@ -175,7 +179,8 @@ namespace host
node_certificate_file,
pid_file,
node_to_node_address_file,
rpc_addresses_file);
rpc_addresses_file,
sealed_ledger_secret_location);

DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(CCHostConfig::Ledger);
DECLARE_JSON_REQUIRED_FIELDS(CCHostConfig::Ledger);
Expand Down Expand Up @@ -214,7 +219,8 @@ namespace host
DECLARE_JSON_OPTIONAL_FIELDS(
CCHostConfig::Command::Recover,
initial_service_certificate_validity_days,
previous_service_identity_file);
previous_service_identity_file,
previous_sealed_ledger_secret_location);

DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(CCHostConfig::Command);
DECLARE_JSON_REQUIRED_FIELDS(CCHostConfig::Command, type);
Expand Down
22 changes: 22 additions & 0 deletions src/host/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache 2.0 License.

#include "ccf/crypto/pem.h"
#include "ccf/crypto/symmetric_key.h"
#include "ccf/ds/logger.h"
#include "ccf/ds/logger_level.h"
#include "ccf/ds/nonstd.h"
Expand All @@ -11,6 +12,7 @@
#include "ccf/pal/attestation.h"
#include "ccf/pal/attestation_sev_snp.h"
#include "ccf/pal/platform.h"
#include "ccf/pal/snp_ioctl.h"
#include "ccf/service/node_info_network.h"
#include "ccf/version.h"
#include "common/configuration.h"
Expand Down Expand Up @@ -711,6 +713,16 @@ int main(int argc, char** argv) // NOLINT(bugprone-exception-escape)
startup_config.startup_host_time =
ccf::ds::to_x509_time_string(startup_host_time);

if (config.output_files.sealed_ledger_secret_location.has_value())
{
CCF_ASSERT_FMT(
ccf::pal::platform == ccf::pal::Platform::SNP,
"Local sealing is only supported on SEV-SNP platforms");
startup_config.network.will_locally_seal_ledger_secrets = true;
startup_config.sealed_ledger_secret_location =
config.output_files.sealed_ledger_secret_location;
}

if (config.command.type == StartType::Start)
{
for (auto const& member : config.command.start.members)
Expand Down Expand Up @@ -796,6 +808,16 @@ int main(int argc, char** argv) // NOLINT(bugprone-exception-escape)
}
LOG_INFO_FMT("Reading previous service identity from {}", idf);
startup_config.recover.previous_service_identity = files::slurp(idf);

if (config.command.recover.previous_sealed_ledger_secret_location
.has_value())
{
CCF_ASSERT_FMT(
ccf::pal::platform == ccf::pal::Platform::SNP,
"Local unsealing is only supported on SEV-SNP platforms");
startup_config.recover.previous_sealed_ledger_secret_location =
config.command.recover.previous_sealed_ledger_secret_location;
}
}
else
{
Expand Down
Loading