Skip to content

Commit 980bf2d

Browse files
authored
igvmfilegen: migrate SNP ID block to serializer + add-snp-id-block command (#3923)
Replace the in-tree signed_measurement module with the igvm crate's IgvmSerializer for launch-measurement computation, and move SNP ID block handling out of the build-time auto-write into an explicit add-snp-id-block subcommand. - Delete signed_measurement/{snp,tdx,vbs} and vbs_defs; compute launch digests via IgvmSerializer::measurement_for. - Add snp_id_block.rs: unsigned artifact emission, temp-key signing (dev/test), and out-of-band DER signature + X.509/SPKI public-key verification (production). - manifest emits <base>-snp.idblock (raw SnpPspIdBlock) for SNP. - Add crypto::ecdsa::verify_prehash (ECDSA P-384 verify) across openssl, symcrypt, and BCrypt backends. - Extract measurement_diag.rs (launch-measurement diagnostics) and platform_mask.rs (compatibility-mask helpers). Signed-off-by: Ming-Wei Shih <mishih@microsoft.com>
1 parent 822d4d4 commit 980bf2d

34 files changed

Lines changed: 2533 additions & 1270 deletions

File tree

Cargo.lock

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -823,9 +823,9 @@ dependencies = [
823823

824824
[[package]]
825825
name = "cmov"
826-
version = "0.5.4"
826+
version = "0.5.3"
827827
source = "registry+https://github.com/rust-lang/crates.io-index"
828-
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
828+
checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746"
829829

830830
[[package]]
831831
name = "cms"
@@ -943,6 +943,28 @@ version = "0.8.7"
943943
source = "registry+https://github.com/rust-lang/crates.io-index"
944944
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
945945

946+
[[package]]
947+
name = "corim"
948+
version = "0.1.3"
949+
source = "registry+https://github.com/rust-lang/crates.io-index"
950+
checksum = "18ae0dacf8af1ae85596c375df3bf848ff57712a8b07d5a9ba36863d901f78d7"
951+
dependencies = [
952+
"corim-macros",
953+
"serde",
954+
"thiserror 2.0.16",
955+
]
956+
957+
[[package]]
958+
name = "corim-macros"
959+
version = "0.1.3"
960+
source = "registry+https://github.com/rust-lang/crates.io-index"
961+
checksum = "3c16ee2c7db3dde66570487ea3a6329bd07ca138652b57a3a442c200c75df495"
962+
dependencies = [
963+
"proc-macro2",
964+
"quote",
965+
"syn 2.0.106",
966+
]
967+
946968
[[package]]
947969
name = "cpubits"
948970
version = "0.1.0"
@@ -3705,24 +3727,27 @@ dependencies = [
37053727
[[package]]
37063728
name = "igvm"
37073729
version = "0.4.0"
3708-
source = "git+https://github.com/microsoft/igvm?rev=3d1a950116d624746bf3342e09ea0a3416d89dd3#3d1a950116d624746bf3342e09ea0a3416d89dd3"
3730+
source = "git+https://github.com/microsoft/igvm?rev=7184dce1086956342c1bf561f82ff268d06d2c73#7184dce1086956342c1bf561f82ff268d06d2c73"
37093731
dependencies = [
37103732
"bitfield-struct 0.12.1",
3733+
"corim",
37113734
"crc32fast",
37123735
"hex",
37133736
"igvm_defs",
37143737
"open-enum",
37153738
"range_map_vec",
3739+
"sha2",
37163740
"static_assertions",
37173741
"thiserror 2.0.16",
37183742
"tracing",
3743+
"uuid",
37193744
"zerocopy",
37203745
]
37213746

37223747
[[package]]
37233748
name = "igvm_defs"
37243749
version = "0.4.0"
3725-
source = "git+https://github.com/microsoft/igvm?rev=3d1a950116d624746bf3342e09ea0a3416d89dd3#3d1a950116d624746bf3342e09ea0a3416d89dd3"
3750+
source = "git+https://github.com/microsoft/igvm?rev=7184dce1086956342c1bf561f82ff268d06d2c73#7184dce1086956342c1bf561f82ff268d06d2c73"
37263751
dependencies = [
37273752
"bitfield-struct 0.12.1",
37283753
"open-enum",
@@ -3736,8 +3761,10 @@ version = "0.0.0"
37363761
dependencies = [
37373762
"anyhow",
37383763
"base64 0.22.1",
3764+
"bitfield-struct 0.11.0",
37393765
"clap",
37403766
"crypto",
3767+
"der",
37413768
"fs-err",
37423769
"hex",
37433770
"hvdef",
@@ -3751,11 +3778,12 @@ dependencies = [
37513778
"range_map_vec",
37523779
"serde",
37533780
"serde_json",
3754-
"thiserror 2.0.16",
3781+
"signature",
3782+
"test_with_tracing",
37553783
"tracing",
37563784
"tracing-subscriber",
37573785
"underhill_confidentiality",
3758-
"vbs_defs",
3786+
"x509-cert",
37593787
"x86defs",
37603788
"zerocopy",
37613789
]
@@ -9130,23 +9158,18 @@ version = "0.2.2"
91309158
source = "registry+https://github.com/rust-lang/crates.io-index"
91319159
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
91329160

9161+
[[package]]
9162+
name = "uuid"
9163+
version = "1.23.3"
9164+
source = "registry+https://github.com/rust-lang/crates.io-index"
9165+
checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7"
9166+
91339167
[[package]]
91349168
name = "valuable"
91359169
version = "0.1.1"
91369170
source = "registry+https://github.com/rust-lang/crates.io-index"
91379171
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
91389172

9139-
[[package]]
9140-
name = "vbs_defs"
9141-
version = "0.0.0"
9142-
dependencies = [
9143-
"bitfield-struct 0.11.0",
9144-
"igvm_defs",
9145-
"open_enum",
9146-
"static_assertions",
9147-
"zerocopy",
9148-
]
9149-
91509173
[[package]]
91519174
name = "vcpkg"
91529175
version = "0.2.15"

Cargo.toml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,6 @@ igvmfilegen_config = { path = "vm/loader/igvmfilegen_config" }
402402
loader_defs = { path = "vm/loader/loader_defs" }
403403
page_table = { path = "vm/loader/page_table" }
404404
page_pool_alloc = { path = "vm/page_pool_alloc" }
405-
vbs_defs = { path = "vm/vbs_defs" }
406405
vmgs = { path = "vm/vmgs/vmgs" }
407406
vmgs_broker = { path = "vm/vmgs/vmgs_broker" }
408407
vmgs_format = { path = "vm/vmgs/vmgs_format" }
@@ -614,8 +613,8 @@ iced-x86 = { version = "1.17", default-features = false, features = [
614613
"no_xop",
615614
"no_d3now",
616615
] }
617-
igvm = { git = "https://github.com/microsoft/igvm", rev = "3d1a950116d624746bf3342e09ea0a3416d89dd3" }
618-
igvm_defs = { git = "https://github.com/microsoft/igvm", rev = "3d1a950116d624746bf3342e09ea0a3416d89dd3", default-features = false }
616+
igvm = { git = "https://github.com/microsoft/igvm", rev = "7184dce1086956342c1bf561f82ff268d06d2c73" }
617+
igvm_defs = { git = "https://github.com/microsoft/igvm", rev = "7184dce1086956342c1bf561f82ff268d06d2c73", default-features = false }
619618
kvm-bindings = "0.14.0"
620619
mshv-bindings = "0.6.8"
621620
mshv-ioctls = "0.6.8"

flowey/flowey_lib_hvlite/src/_jobs/local_build_igvm.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,7 @@ impl SimpleFlowNode for Node {
310310
igvm_tdx_json,
311311
igvm_snp_json,
312312
igvm_vbs_json,
313+
igvm_snp_idblock,
313314
}) = openhcl_igvm.endorsements()
314315
{
315316
if let Some(igvm_tdx_json) = igvm_tdx_json {
@@ -321,6 +322,9 @@ impl SimpleFlowNode for Node {
321322
if let Some(igvm_vbs_json) = igvm_vbs_json {
322323
fs_err::copy(igvm_vbs_json, output_dir.join("openhcl-vbs.json"))?;
323324
}
325+
if let Some(igvm_snp_idblock) = igvm_snp_idblock {
326+
fs_err::copy(igvm_snp_idblock, output_dir.join("openhcl-snp.idblock"))?;
327+
}
324328
}
325329
for e in fs_err::read_dir(output_dir)? {
326330
let e = e?;

flowey/flowey_lib_hvlite/src/build_openhcl_igvm_from_recipe.rs

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ pub enum OpenhclIgvmEndorsements {
8888
#[serde(rename = "openhcl-vbs.json")]
8989
#[serde(skip_serializing_if = "Option::is_none")]
9090
igvm_vbs_json: Option<PathBuf>,
91+
/// The unsigned SNP ID block signing payload emitted by `manifest`.
92+
/// This travels with the endorsements (rather than the debug-symbol
93+
/// extras) because it is a release/signing input consumed alongside the
94+
/// `.json` identity documents. SNP-only, so absent for other platforms.
95+
#[serde(rename = "openhcl-snp.idblock")]
96+
#[serde(skip_serializing_if = "Option::is_none")]
97+
igvm_snp_idblock: Option<PathBuf>,
9198
},
9299
}
93100

@@ -98,6 +105,10 @@ impl OpenhclIgvmEndorsements {
98105
igvm_tdx_json,
99106
igvm_snp_json,
100107
igvm_vbs_json,
108+
// Not part of completeness: the SNP ID block signing payload is
109+
// an optional, SNP-only signing input, not one of the three
110+
// required identity-document endorsements.
111+
igvm_snp_idblock: _,
101112
} => igvm_tdx_json.is_some() && igvm_snp_json.is_some() && igvm_vbs_json.is_some(),
102113
}
103114
}
@@ -154,17 +165,22 @@ impl OpenhclIgvmOutput {
154165
igvm_tdx_json,
155166
igvm_snp_json,
156167
igvm_vbs_json,
168+
igvm_snp_idblock,
157169
} = igvm;
158-
let mut endorsements =
159-
if igvm_tdx_json.is_some() || igvm_snp_json.is_some() || igvm_vbs_json.is_some() {
160-
Some(OpenhclIgvmEndorsements::X64 {
161-
igvm_tdx_json,
162-
igvm_snp_json,
163-
igvm_vbs_json,
164-
})
165-
} else {
166-
None
167-
};
170+
let mut endorsements = if igvm_tdx_json.is_some()
171+
|| igvm_snp_json.is_some()
172+
|| igvm_vbs_json.is_some()
173+
|| igvm_snp_idblock.is_some()
174+
{
175+
Some(OpenhclIgvmEndorsements::X64 {
176+
igvm_tdx_json,
177+
igvm_snp_json,
178+
igvm_vbs_json,
179+
igvm_snp_idblock,
180+
})
181+
} else {
182+
None
183+
};
168184
match recipe {
169185
None => OpenhclIgvmOutput::LocalOnlyCustom {
170186
igvm_bin,
@@ -914,6 +930,10 @@ impl SimpleFlowNode for Node {
914930
resources,
915931
disable_secure_avic,
916932
confidential_debug,
933+
// Open-source builds embed a temporary-key SNP ID block so the file
934+
// launches with `id_block_en = 1`; production pipelines add a
935+
// real-key ID block out-of-band instead.
936+
add_temp_snp_id_block: true,
917937
igvm: v,
918938
});
919939

flowey/flowey_lib_hvlite/src/run_igvmfilegen.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ pub struct IgvmOutput {
1515
pub igvm_tdx_json: Option<PathBuf>,
1616
pub igvm_snp_json: Option<PathBuf>,
1717
pub igvm_vbs_json: Option<PathBuf>,
18+
/// The unsigned SNP ID block signing payload (`<base>-snp.idblock`), if the
19+
/// manifest produced a measurable SEV-SNP platform.
20+
pub igvm_snp_idblock: Option<PathBuf>,
1821
}
1922

2023
flowey_request! {
@@ -31,6 +34,13 @@ flowey_request! {
3134
/// command line, enabling confidential diagnostics on CVM builds even
3235
/// in release builds.
3336
pub confidential_debug: bool,
37+
/// For SEV-SNP builds, add an SNP ID block signed by an ephemeral key
38+
/// (via `igvmfilegen add-snp-id-block --manifest`). This restores the
39+
/// pre-migration behavior for open-source builds, where every SNP IGVM
40+
/// carried a temporary-key ID block so that `id_block_en = 1` at launch.
41+
/// Production pipelines set this to `false` and instead add an ID block
42+
/// signed with a real key out-of-band.
43+
pub add_temp_snp_id_block: bool,
3444
/// Output path of generated igvm file
3545
pub igvm: WriteVar<IgvmOutput>,
3646
}
@@ -50,6 +60,7 @@ impl SimpleFlowNode for Node {
5060
resources,
5161
disable_secure_avic,
5262
confidential_debug,
63+
add_temp_snp_id_block,
5364
igvm,
5465
} = request;
5566

@@ -106,6 +117,27 @@ impl SimpleFlowNode for Node {
106117
let path = igvm_path.with_file_name(format!("{igvm_file_stem}-vbs.json"));
107118
path.exists().then_some(path)
108119
};
120+
let igvm_snp_idblock = {
121+
let path = igvm_path.with_file_name(format!("{igvm_file_stem}-snp.idblock"));
122+
path.exists().then_some(path)
123+
};
124+
125+
// For open-source SEV-SNP builds, embed an ID block signed by an
126+
// ephemeral key so the file launches with `id_block_en = 1`, as
127+
// it did before the ID block was split into a separate step.
128+
// The presence of `<stem>-snp.idblock` means the manifest built
129+
// a measurable SNP platform.
130+
if add_temp_snp_id_block && igvm_snp_idblock.is_some() {
131+
flowey::shell_cmd!(
132+
rt,
133+
"{igvmfilegen} add-snp-id-block
134+
--input {igvm_path}
135+
--output {igvm_path}
136+
--manifest {manifest}
137+
"
138+
)
139+
.run()?;
140+
}
109141

110142
rt.write(
111143
igvm,
@@ -115,6 +147,7 @@ impl SimpleFlowNode for Node {
115147
igvm_tdx_json,
116148
igvm_snp_json,
117149
igvm_vbs_json,
150+
igvm_snp_idblock,
118151
},
119152
);
120153

openhcl/underhill_attestation/src/jwt.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ pub(crate) enum CertificateChainValidationError {
5353
#[error("certificate chain is empty")]
5454
CertChainIsEmpty,
5555
#[error("failed to get public key from the certificate")]
56-
GetPublicKeyFromCertificate(#[source] crypto::rsa::RsaError),
56+
GetPublicKeyFromCertificate(#[source] crypto::x509::X509Error),
57+
#[error("certificate public key is not an RSA key")]
58+
PublicKeyNotRsa,
5759
#[error("failed to verify the child certificate signature with parent public key")]
5860
VerifyChildSignatureWithParentPublicKey(#[source] crypto::rsa::RsaError),
5961
#[error("cert chain validation failed -- signature mismatch")]
@@ -244,7 +246,9 @@ fn validate_cert_chain(
244246
let parent = &cert_chain[i + 1];
245247
let public_key = parent
246248
.public_key()
247-
.map_err(CertificateChainValidationError::GetPublicKeyFromCertificate)?;
249+
.map_err(CertificateChainValidationError::GetPublicKeyFromCertificate)?
250+
.rsa()
251+
.ok_or(CertificateChainValidationError::PublicKeyNotRsa)?;
248252

249253
let verified = child.verify(&public_key).map_err(
250254
CertificateChainValidationError::VerifyChildSignatureWithParentPublicKey,
@@ -264,7 +268,9 @@ fn validate_cert_chain(
264268

265269
cert_chain[0]
266270
.public_key()
267-
.map_err(CertificateChainValidationError::GetPublicKeyFromCertificate)
271+
.map_err(CertificateChainValidationError::GetPublicKeyFromCertificate)?
272+
.rsa()
273+
.ok_or(CertificateChainValidationError::PublicKeyNotRsa)
268274
}
269275

270276
#[cfg(test)]
@@ -400,7 +406,7 @@ mod tests {
400406
.unwrap();
401407

402408
let cert = crate::test_helpers::generate_x509(&rsa_key);
403-
let public = cert.public_key().unwrap();
409+
let public = cert.public_key().unwrap().rsa().unwrap();
404410

405411
let verification_succeeded = verify_jwt_signature(
406412
&JwtAlgorithm::RS256,

support/crypto/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ signature = { workspace = true, features = ["alloc"] }
107107
x509-cert.workspace = true
108108

109109
[dev-dependencies]
110+
der = { workspace = true, features = ["derive", "alloc"] }
110111
hex.workspace = true
111112
x509-cert = { workspace = true, features = ["builder"] }
112113

0 commit comments

Comments
 (0)