From 664e406c66486a8ffb0892789fd8a2deab607559 Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 18 Dec 2024 17:50:15 +0100 Subject: [PATCH] Extract PCK certificate chain from quotes (#1209) * WIP move PCK cert chain verification to attestation pallet * Switch things around for PCK cert chain verification happenning as part of quote verification * Fix error handling in entropy-tss * Use git version of tdx-quote * Use git version of tdx-quote * Update node/cli/Cargo.toml Co-authored-by: Hernando Castano * Fix staking pallet cargo.toml * Taplo * Update tdx-quote, rm unwraps * Improve error handling in attestation pallet * Bump tdx-quote again following improvement of error type * Update tdx-quote following pck_verify takes reference * Revert cargo.lock, fix ensure! macro call * Fix staking pallet config in other pallets mock files * Update metadata * Fix client test * Update test-cli * Fixes for tests * Fix staking pallet benchmarking * Fix staking pallet benchmarking * Fix client following merge master * Typo * Error handling * Update staking pallet tests * Comments, changelog * VerifyQuoteError implements Debug * Rm PCK cert chain from JoiningServerInfo * Pull chain metadata * Clippy * Clippy * Add to changelog * Minor changes following review comments * Use specific commit of tdx-quote rather than branch --------- Co-authored-by: Hernando Castano --- CHANGELOG.md | 8 ++ Cargo.lock | 20 ++- crates/client/Cargo.toml | 8 +- crates/client/entropy_metadata.scale | Bin 210103 -> 210681 bytes crates/client/src/client.rs | 4 - crates/client/src/tests.rs | 19 +-- crates/shared/src/types.rs | 37 +++++- crates/test-cli/src/lib.rs | 6 - crates/testing-utils/Cargo.toml | 36 ++--- crates/threshold-signature-server/Cargo.toml | 54 ++++---- .../src/attestation/api.rs | 5 +- .../src/attestation/errors.rs | 3 + .../src/attestation/tests.rs | 2 +- node/cli/Cargo.toml | 3 + pallets/attestation/Cargo.toml | 24 ++-- pallets/attestation/src/lib.rs | 69 ++++++---- pallets/attestation/src/mock.rs | 1 - pallets/attestation/src/tests.rs | 44 +++---- .../test_pck_certs/pck_cert.der | Bin .../test_pck_certs/platform_pcs_cert.der | Bin pallets/propagation/src/mock.rs | 1 - pallets/registry/src/mock.rs | 1 - pallets/staking/Cargo.toml | 13 +- pallets/staking/src/benchmarking.rs | 46 ++++--- pallets/staking/src/lib.rs | 123 +++++++++--------- pallets/staking/src/mock.rs | 12 +- ..._SGX_Provisioning_Certification_RootCA.cer | Bin 659 -> 0 bytes pallets/staking/src/pck/mock.rs | 56 -------- pallets/staking/src/pck/mod.rs | 58 --------- pallets/staking/src/pck/production.rs | 110 ---------------- pallets/staking/src/tests.rs | 100 ++++---------- runtime/Cargo.toml | 3 + runtime/src/lib.rs | 1 - 33 files changed, 326 insertions(+), 541 deletions(-) rename pallets/{staking => attestation}/test_pck_certs/pck_cert.der (100%) rename pallets/{staking => attestation}/test_pck_certs/platform_pcs_cert.der (100%) delete mode 100644 pallets/staking/src/pck/Intel_SGX_Provisioning_Certification_RootCA.cer delete mode 100644 pallets/staking/src/pck/mock.rs delete mode 100644 pallets/staking/src/pck/mod.rs delete mode 100644 pallets/staking/src/pck/production.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f17c8377..f3ce565ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,10 @@ runtime - In [#1147](https://github.com/entropyxyz/entropy-core/pull/1147) a field is added to the chainspec: `jump_started_signers` which allows the chain to be started in a pre-jumpstarted state for testing. If this is not desired it should be set to `None`. +- In [#1209](https://github.com/entropyxyz/entropy-core/pull/1209) the `validate` and `change_threshold_accounts` + extrinsics no longer take a PCK certificate chain. Rather, the certificate chain is extracted from the + provided quote. The test CLI `change-threshold-accounts` command also no longer takes a PCK + certificate chain. ### Added - In [#1128](https://github.com/entropyxyz/entropy-core/pull/1128) an `/info` route was added to `entropy-tss` @@ -41,6 +45,9 @@ runtime - Add TSS endpoint to get TDX quote ([#1173](https://github.com/entropyxyz/entropy-core/pull/1173)) - Add TDX test network chainspec ([#1204](https://github.com/entropyxyz/entropy-core/pull/1204)) - Test CLI command to retrieve quote and change endpoint / TSS account in one command ([#1198](https://github.com/entropyxyz/entropy-core/pull/1198)) +- In ([#1209]()) a `production` feature flag was added to `entropy` which if enabled will use + non-mock verification of PCK certificate chains in TDX quotes, meaning TSS servers must be running + on TDX hardware - On-chain unresponsiveness reporting [(#1215)](https://github.com/entropyxyz/entropy-core/pull/1215) ### Changed @@ -52,6 +59,7 @@ runtime - Update programs to accept multiple oracle data ([#1153](https://github.com/entropyxyz/entropy-core/pull/1153/)) - Use context, not block number in TDX quote input data ([#1179](https://github.com/entropyxyz/entropy-core/pull/1179)) - Allow offchain worker requests to all TSS nodes in entropy-tss test environment ([#1147](https://github.com/entropyxyz/entropy-core/pull/1147)) +- Extract PCK certificate chain from quotes ([#1209](https://github.com/entropyxyz/entropy-core/pull/1209)) ### Fixed diff --git a/Cargo.lock b/Cargo.lock index d7d0e9ea8..87c185317 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7492,7 +7492,6 @@ dependencies = [ "frame-support 29.0.2", "frame-system", "log", - "p256", "pallet-bags-list", "pallet-balances", "pallet-parameters", @@ -7514,9 +7513,7 @@ dependencies = [ "sp-runtime 32.0.0", "sp-staking 27.0.0", "sp-std 14.0.0", - "spki", "tdx-quote", - "x509-verify", ] [[package]] @@ -7916,6 +7913,15 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -8758,7 +8764,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b" dependencies = [ - "pem", + "pem 1.1.1", "ring 0.16.20", "time", "yasna", @@ -14281,12 +14287,14 @@ checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "tdx-quote" version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10fe3140395153ccb4050f6eb51fe621a3b6d76da489601aec9fabe2fb1c3e0" +source = "git+https://github.com/entropyxyz/tdx-quote.git?rev=67a9d011809d0c9109d1ac42aeb809a84b663be6#67a9d011809d0c9109d1ac42aeb809a84b663be6" dependencies = [ "nom", "p256", + "pem 3.0.4", "sha2 0.10.8", + "spki", + "x509-verify", ] [[package]] diff --git a/crates/client/Cargo.toml b/crates/client/Cargo.toml index b07b110d1..0161ad229 100644 --- a/crates/client/Cargo.toml +++ b/crates/client/Cargo.toml @@ -38,10 +38,12 @@ js-sys={ version="0.3.74", optional=true } tokio ={ version="1.42", features=["time"] } [dev-dependencies] -serial_test ="3.2.0" -sp-keyring ="34.0.0" +serial_test="3.2.0" +sp-keyring="34.0.0" entropy-testing-utils={ path="../testing-utils" } -tdx-quote ={ version="0.0.1", features=["mock"] } +tdx-quote={ git="https://github.com/entropyxyz/tdx-quote.git", rev="67a9d011809d0c9109d1ac42aeb809a84b663be6", features=[ + "mock", +] } [features] default=["native", "full-client-native"] diff --git a/crates/client/entropy_metadata.scale b/crates/client/entropy_metadata.scale index 9d94e92bcafb91bedcad567f69379041060a5b66..592a37ddec70dae2e6a6ff3f88f9de6a70b28494 100644 GIT binary patch delta 572 zcmdn~lIQ1Jo(&2w8F?lvzI55#`*I!=kg4+~YV*1`meUw>w!dg%>|o?pa7s)GEX^-T zWe}V`=O?2bqtNu*KN&q3g{Q0hVl?45080B7m866v=9H!~h)kFN&8TMXnwOlPk{Xs; zl$lnUnV0UJTFD^F(&C($mzQ6nkP4PmD1*r=WT#dtXaqQWYfiWO%_zkvHa+q;qw@5E z7Dmx5ah4fC35Dd;qLR$C%;dz9RE6Y>#LPT}WQbWwsR{*&Ma8Kp0RhR`&InZys~IF% z77)@7c1C7u%Jlgyj521DEN8H5C@4+J$xH@2HM3X&q1FMxE^we==@i z#1?pLL%qn*2jUp_Q=oI!!dz{PR_yDt%;ip{a@JbxIs K^Za32Z2kPAo}{PtHip%)7|QXb_f~Yy-lj z7It=lzxGS>, ) -> Result { let quote = get_tdx_quote_with_validator_id( api, @@ -387,7 +386,6 @@ pub async fn get_quote_and_change_threshold_accounts( validator_keypair, new_tss_account, new_x25519_public_key, - new_pck_certificate_chain, quote, ) .await @@ -400,13 +398,11 @@ pub async fn change_threshold_accounts( validator_keypair: sr25519::Pair, new_tss_account: SubxtAccountId32, new_x25519_public_key: [u8; 32], - new_pck_certificate_chain: Vec>, quote: Vec, ) -> Result { let change_threshold_accounts = entropy::tx().staking_extension().change_threshold_accounts( new_tss_account, new_x25519_public_key, - new_pck_certificate_chain, quote, ); let in_block = submit_transaction_with_pair( diff --git a/crates/client/src/tests.rs b/crates/client/src/tests.rs index a42b7758e..bf3870102 100644 --- a/crates/client/src/tests.rs +++ b/crates/client/src/tests.rs @@ -59,8 +59,11 @@ async fn test_change_endpoint() { let mut pck_seeder = StdRng::from_seed(public_key.0); let pck = tdx_quote::SigningKey::random(&mut pck_seeder); + let pck_encoded = tdx_quote::encode_verifying_key(pck.verifying_key()).unwrap().to_vec(); - tdx_quote::Quote::mock(signing_key.clone(), pck, input_data.0).as_bytes().to_vec() + tdx_quote::Quote::mock(signing_key.clone(), pck, input_data.0, pck_encoded) + .as_bytes() + .to_vec() }; let result = @@ -116,14 +119,12 @@ async fn test_change_threshold_accounts() { let nonce = request_attestation(&api, &rpc, tss_signer_pair.signer()).await.unwrap(); let nonce: [u8; 32] = nonce.try_into().unwrap(); - let mut pck_seeder = StdRng::from_seed(tss_public_key.0.clone()); - let pck = tdx_quote::SigningKey::random(&mut pck_seeder); - let encoded_pck = encode_verifying_key(&pck.verifying_key()).unwrap().to_vec(); - // Our runtime is using the mock `PckCertChainVerifier`, which means that the expected // "certificate" basically is just our TSS account ID. This account needs to match the one // used to sign the following `quote`. - let pck_certificate_chain = vec![tss_public_key.0.to_vec()]; + let mut pck_seeder = StdRng::from_seed(tss_public_key.0.clone()); + let pck = tdx_quote::SigningKey::random(&mut pck_seeder); + let encoded_pck = encode_verifying_key(&pck.verifying_key()).unwrap().to_vec(); let quote = { let input_data = entropy_shared::QuoteInputData::new( @@ -134,7 +135,10 @@ async fn test_change_threshold_accounts() { ); let signing_key = tdx_quote::SigningKey::random(&mut OsRng); - tdx_quote::Quote::mock(signing_key.clone(), pck.clone(), input_data.0).as_bytes().to_vec() + + tdx_quote::Quote::mock(signing_key.clone(), pck.clone(), input_data.0, encoded_pck.clone()) + .as_bytes() + .to_vec() }; let result = change_threshold_accounts( @@ -143,7 +147,6 @@ async fn test_change_threshold_accounts() { one.into(), tss_public_key.into(), *x25519_public_key.as_bytes(), - pck_certificate_chain, quote, ) .await diff --git a/crates/shared/src/types.rs b/crates/shared/src/types.rs index 2de1b647c..a9655c760 100644 --- a/crates/shared/src/types.rs +++ b/crates/shared/src/types.rs @@ -162,13 +162,14 @@ impl std::fmt::Display for QuoteContext { #[cfg(not(feature = "wasm"))] pub trait AttestationHandler { /// Verify that the given quote is valid and matches the given information about the attestee. + /// The Provisioning Certification Key (PCK) certifcate chain is extracted from the quote and + /// verified. If successful, the PCK public key used to sign the quote is returned. fn verify_quote( attestee: &AccountId, x25519_public_key: X25519PublicKey, - provisioning_certification_key: BoundedVecEncodedVerifyingKey, quote: Vec, context: QuoteContext, - ) -> Result<(), sp_runtime::DispatchError>; + ) -> Result; /// Indicate to the attestation handler that a quote is desired. /// @@ -183,12 +184,38 @@ impl AttestationHandler for () { fn verify_quote( _attestee: &AccountId, _x25519_public_key: X25519PublicKey, - _provisioning_certification_key: BoundedVecEncodedVerifyingKey, _quote: Vec, _context: QuoteContext, - ) -> Result<(), sp_runtime::DispatchError> { - Ok(()) + ) -> Result { + // Ok(sp_runtime::BoundedVec::new()) + Ok(BoundedVecEncodedVerifyingKey::try_from([0; 33].to_vec()).unwrap()) } fn request_quote(_attestee: &AccountId, _nonce: [u8; 32]) {} } + +/// An error when verifying a quote +#[cfg(not(feature = "wasm"))] +#[derive(Debug, Eq, PartialEq)] +pub enum VerifyQuoteError { + /// Quote could not be parsed or verified + BadQuote, + /// Attestation extrinsic submitted when not requested + UnexpectedAttestation, + /// Hashed input data does not match what was expected + IncorrectInputData, + /// Unacceptable VM image running + BadMrtdValue, + /// Cannot encode verifying key (PCK) + CannotEncodeVerifyingKey, + /// Cannot decode verifying key (PCK) + CannotDecodeVerifyingKey, + /// PCK certificate chain cannot be parsed + PckCertificateParse, + /// PCK certificate chain cannot be verified + PckCertificateVerify, + /// PCK certificate chain public key is not well formed + PckCertificateBadPublicKey, + /// Pck certificate could not be extracted from quote + PckCertificateNoCertificate, +} diff --git a/crates/test-cli/src/lib.rs b/crates/test-cli/src/lib.rs index 42a030545..731ab0812 100644 --- a/crates/test-cli/src/lib.rs +++ b/crates/test-cli/src/lib.rs @@ -160,8 +160,6 @@ enum CliCommand { new_tss_account: String, /// New x25519 public key new_x25519_public_key: String, - /// The new Provisioning Certification Key (PCK) certificate chain to be used for the TSS. - new_pck_certificate_chain: Vec, /// The mnemonic for the validator stash account to use for the call, should be stash address #[arg(short, long)] mnemonic_option: Option, @@ -482,7 +480,6 @@ pub async fn run_command( CliCommand::ChangeThresholdAccounts { new_tss_account, new_x25519_public_key, - new_pck_certificate_chain, mnemonic_option, } => { let user_keypair = handle_mnemonic(mnemonic_option)?; @@ -492,15 +489,12 @@ pub async fn run_command( let new_x25519_public_key = hex::decode(new_x25519_public_key)? .try_into() .map_err(|_| anyhow!("X25519 pub key needs to be 32 bytes"))?; - let new_pck_certificate_chain = - new_pck_certificate_chain.iter().cloned().map(|i| i.into()).collect::<_>(); let result_event = get_quote_and_change_threshold_accounts( &api, &rpc, user_keypair, new_tss_account, new_x25519_public_key, - new_pck_certificate_chain, ) .await?; cli.log(format!("Event result: {:?}", result_event)); diff --git a/crates/testing-utils/Cargo.toml b/crates/testing-utils/Cargo.toml index 65c6fa263..75d0d0755 100644 --- a/crates/testing-utils/Cargo.toml +++ b/crates/testing-utils/Cargo.toml @@ -9,24 +9,26 @@ repository ='https://github.com/entropyxyz/entropy-core' edition ='2021' [dependencies] -subxt ="0.35.3" -sp-keyring ="34.0.0" -project-root ="0.2.2" -sp-core ={ version="31.0.0", default-features=false } +subxt="0.35.3" +sp-keyring="34.0.0" +project-root="0.2.2" +sp-core={ version="31.0.0", default-features=false } parity-scale-codec="3.6.12" -lazy_static ="1.5.0" -hex-literal ="0.4.1" -tokio ={ version="1.42", features=["macros", "fs", "rt-multi-thread", "io-util", "process"] } -axum ={ version="0.7.9" } -entropy-shared ={ version="0.3.0", path="../shared" } -entropy-kvdb ={ version="0.3.0", path="../kvdb", default-features=false } -entropy-tss ={ version="0.3.0", path="../threshold-signature-server", features=["test_helpers"] } -entropy-protocol ={ version="0.3.0", path="../protocol" } -synedrion ="0.2.0" -hex ="0.4.3" -rand_core ="0.6.4" -rand ="0.8.5" -tdx-quote ={ version="0.0.1", features=["mock"] } +lazy_static="1.5.0" +hex-literal="0.4.1" +tokio={ version="1.42", features=["macros", "fs", "rt-multi-thread", "io-util", "process"] } +axum={ version="0.7.9" } +entropy-shared={ version="0.3.0", path="../shared" } +entropy-kvdb={ version="0.3.0", path="../kvdb", default-features=false } +entropy-tss={ version="0.3.0", path="../threshold-signature-server", features=["test_helpers"] } +entropy-protocol={ version="0.3.0", path="../protocol" } +synedrion="0.2.0" +hex="0.4.3" +rand_core="0.6.4" +rand="0.8.5" +tdx-quote={ git="https://github.com/entropyxyz/tdx-quote.git", rev="67a9d011809d0c9109d1ac42aeb809a84b663be6", features=[ + "mock", +] } # Logging tracing ="0.1.41" diff --git a/crates/threshold-signature-server/Cargo.toml b/crates/threshold-signature-server/Cargo.toml index 090041cfd..be5cdf564 100644 --- a/crates/threshold-signature-server/Cargo.toml +++ b/crates/threshold-signature-server/Cargo.toml @@ -58,36 +58,40 @@ uuid ={ version="1.11.0", features=["v4"] } # Misc tokio-tungstenite="0.24.0" -bincode ="1.3.3" -bip32 ={ version="0.5.2" } -bip39 ={ version="2.1.0", features=["zeroize"] } -bytes ={ version="1.9", default-features=false, features=["serde"] } -base64 ="0.22.1" -clap ={ version="4.5.23", features=["derive"] } -num ="0.4.3" -snow ="0.9.6" -sha3 ="0.10.8" -hostname ="0.4" -sha1 ="0.10.6" -sha2 ="0.10.8" -hkdf ="0.12.4" -project-root ={ version="0.2.2", optional=true } -tdx-quote ={ version="0.0.1", optional=true, features=["mock"] } -configfs-tsm ={ version="0.0.1", optional=true } +bincode="1.3.3" +bip32={ version="0.5.2" } +bip39={ version="2.1.0", features=["zeroize"] } +bytes={ version="1.9", default-features=false, features=["serde"] } +base64="0.22.1" +clap={ version="4.5.23", features=["derive"] } +num="0.4.3" +snow="0.9.6" +sha3="0.10.8" +hostname="0.4" +sha1="0.10.6" +sha2="0.10.8" +hkdf="0.12.4" +project-root={ version="0.2.2", optional=true } +tdx-quote={ git="https://github.com/entropyxyz/tdx-quote.git", rev="67a9d011809d0c9109d1ac42aeb809a84b663be6", optional=true, features=[ + "mock", +] } +configfs-tsm={ version="0.0.1", optional=true } [dev-dependencies] -serial_test ="3.2.0" -hex-literal ="0.4.1" +serial_test="3.2.0" +hex-literal="0.4.1" project-root="0.2.2" -sp-keyring ="34.0.0" +sp-keyring="34.0.0" more-asserts="0.3.1" -lazy_static ="1.5.0" -blake3 ="1.5.5" -ethers-core ="2.0.14" -schnorrkel ={ version="0.11.4", default-features=false, features=["std"] } -schemars ={ version="0.8.21" } +lazy_static="1.5.0" +blake3="1.5.5" +ethers-core="2.0.14" +schnorrkel={ version="0.11.4", default-features=false, features=["std"] } +schemars={ version="0.8.21" } subxt-signer="0.35.3" -tdx-quote ={ version="0.0.1", features=["mock"] } +tdx-quote={ git="https://github.com/entropyxyz/tdx-quote.git", rev="67a9d011809d0c9109d1ac42aeb809a84b663be6", features=[ + "mock", +] } # Note: We don't specify versions here because otherwise we run into a cyclical dependency between # `entropy-tss` and `entropy-testing-utils` when we try and publish the `entropy-tss` crate. diff --git a/crates/threshold-signature-server/src/attestation/api.rs b/crates/threshold-signature-server/src/attestation/api.rs index d043e149a..90bc302a8 100644 --- a/crates/threshold-signature-server/src/attestation/api.rs +++ b/crates/threshold-signature-server/src/attestation/api.rs @@ -136,7 +136,10 @@ pub async fn create_quote( let mut pck_seeder = StdRng::from_seed(signer.signer().public().0); let pck = tdx_quote::SigningKey::random(&mut pck_seeder); - let quote = tdx_quote::Quote::mock(signing_key.clone(), pck, input_data.0).as_bytes().to_vec(); + let pck_encoded = tdx_quote::encode_verifying_key(pck.verifying_key())?.to_vec(); + let quote = tdx_quote::Quote::mock(signing_key.clone(), pck, input_data.0, pck_encoded) + .as_bytes() + .to_vec(); Ok(quote) } diff --git a/crates/threshold-signature-server/src/attestation/errors.rs b/crates/threshold-signature-server/src/attestation/errors.rs index 7daad52fb..0905ceb27 100644 --- a/crates/threshold-signature-server/src/attestation/errors.rs +++ b/crates/threshold-signature-server/src/attestation/errors.rs @@ -40,6 +40,9 @@ pub enum AttestationErr { #[cfg(feature = "production")] #[error("Quote generation: {0}")] QuoteGeneration(String), + #[cfg(not(feature = "production"))] + #[error("Cannot encode verifying key: {0}")] + EncodeVerifyingKey(#[from] tdx_quote::VerifyingKeyError), #[error("Vec Conversion Error: {0}")] Conversion(&'static str), #[error("Data is repeated")] diff --git a/crates/threshold-signature-server/src/attestation/tests.rs b/crates/threshold-signature-server/src/attestation/tests.rs index 8ba451305..ae68641f8 100644 --- a/crates/threshold-signature-server/src/attestation/tests.rs +++ b/crates/threshold-signature-server/src/attestation/tests.rs @@ -63,7 +63,7 @@ async fn test_get_attest() { decode_verifying_key(&server_info.provisioning_certification_key.0.try_into().unwrap()) .unwrap(); - assert!(quote.verify_with_pck(provisioning_certification_key).is_ok()) + assert!(quote.verify_with_pck(&provisioning_certification_key).is_ok()) } #[ignore] diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index b1cf2d0cb..e3fb11609 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -119,3 +119,6 @@ runtime-benchmarks=[ # Enable features that allow the runtime to be tried and debugged. Name might be subject to change # in the near future. try-runtime=["entropy-runtime/try-runtime", "try-runtime-cli/try-runtime"] +# Enables real PCK certificate chain verification - which means TSS nodes must be runnning on TDX +# hardware +production=["entropy-runtime/production"] diff --git a/pallets/attestation/Cargo.toml b/pallets/attestation/Cargo.toml index 917fdf320..30f7b239b 100644 --- a/pallets/attestation/Cargo.toml +++ b/pallets/attestation/Cargo.toml @@ -28,19 +28,21 @@ entropy-shared={ version="0.3.0", path="../../crates/shared", features=[ "wasm-no-std", ], default-features=false } pallet-staking-extension={ version="0.3.0", path="../staking", default-features=false } -tdx-quote="0.0.1" +tdx-quote={ git="https://github.com/entropyxyz/tdx-quote.git", rev="67a9d011809d0c9109d1ac42aeb809a84b663be6" } [dev-dependencies] -pallet-session ={ version="29.0.0", default-features=false } -pallet-staking ={ version="29.0.0", default-features=false } -pallet-balances ={ version="29.0.0", default-features=false } -pallet-bags-list ={ version="28.0.0", default-features=false } -pallet-timestamp ={ version="28.0.0", default-features=false } -sp-npos-elections ={ version="27.0.0", default-features=false } +pallet-session={ version="29.0.0", default-features=false } +pallet-staking={ version="29.0.0", default-features=false } +pallet-balances={ version="29.0.0", default-features=false } +pallet-bags-list={ version="28.0.0", default-features=false } +pallet-timestamp={ version="28.0.0", default-features=false } +sp-npos-elections={ version="27.0.0", default-features=false } frame-election-provider-support={ version="29.0.0", default-features=false } -pallet-staking-reward-curve ={ version="11.0.0" } -tdx-quote ={ version="0.0.1", features=["mock"] } -rand_core ="0.6.4" +pallet-staking-reward-curve={ version="11.0.0" } +tdx-quote={ git="https://github.com/entropyxyz/tdx-quote.git", rev="67a9d011809d0c9109d1ac42aeb809a84b663be6", features=[ + "mock", +] } +rand_core="0.6.4" pallet-slashing={ version="0.3.0", path="../slashing", default-features=false } @@ -60,3 +62,5 @@ std=[ "rand_chacha/std", ] try-runtime=['frame-support/try-runtime'] +# When enabled, use real PCK certificate chain verification +production=[] diff --git a/pallets/attestation/src/lib.rs b/pallets/attestation/src/lib.rs index ec046b851..1d4f08c6f 100644 --- a/pallets/attestation/src/lib.rs +++ b/pallets/attestation/src/lib.rs @@ -47,7 +47,7 @@ mod tests; #[frame_support::pallet] pub mod pallet { - use entropy_shared::{AttestationHandler, QuoteContext, QuoteInputData}; + use entropy_shared::{AttestationHandler, QuoteContext, QuoteInputData, VerifyQuoteError}; use frame_support::pallet_prelude::*; use frame_support::traits::Randomness; use frame_system::pallet_prelude::*; @@ -58,7 +58,7 @@ pub mod pallet { rand_core::{RngCore, SeedableRng}, ChaCha20Rng, ChaChaRng, }; - use tdx_quote::{decode_verifying_key, Quote}; + use tdx_quote::{encode_verifying_key, Quote, VerifyingKey}; pub use crate::weights::WeightInfo; @@ -133,12 +133,16 @@ pub mod pallet { NoPCKForAccount, /// Unacceptable VM image running BadMrtdValue, + /// Cannot encode verifying key (PCK) + CannotEncodeVerifyingKey, /// Cannot decode verifying key (PCK) CannotDecodeVerifyingKey, /// Could not verify PCK signature PckVerification, /// There's an existing attestation request for this account ID. OutstandingAttestationRequest, + /// PCK certificate chain cannot be extracted from quote + NoPckCertChain, } #[pallet::call] @@ -203,54 +207,73 @@ pub mod pallet { fn verify_quote( attestee: &T::AccountId, x25519_public_key: entropy_shared::X25519PublicKey, - provisioning_certification_key: entropy_shared::BoundedVecEncodedVerifyingKey, quote: Vec, context: QuoteContext, - ) -> Result<(), DispatchError> { + ) -> Result { // Check that we were expecting a quote from this validator by getting the associated // nonce from PendingAttestations. - let nonce = - PendingAttestations::::get(attestee).ok_or(Error::::UnexpectedAttestation)?; + let nonce = PendingAttestations::::get(attestee) + .ok_or(VerifyQuoteError::UnexpectedAttestation)?; // Parse the quote (which internally verifies the attestation key signature) - let quote = Quote::from_bytes("e).map_err(|_| Error::::BadQuote)?; + let quote = Quote::from_bytes("e).map_err(|_| VerifyQuoteError::BadQuote)?; // Check report input data matches the nonce, TSS details and block number let expected_input_data = QuoteInputData::new(attestee, x25519_public_key, nonce, context); ensure!( quote.report_input_data() == expected_input_data.0, - Error::::IncorrectInputData + VerifyQuoteError::IncorrectInputData ); // Check build-time measurement matches a current-supported release of entropy-tss let mrtd_value = BoundedVec::try_from(quote.mrtd().to_vec()) - .map_err(|_| Error::::BadMrtdValue)?; + .map_err(|_| VerifyQuoteError::BadMrtdValue)?; let accepted_mrtd_values = pallet_parameters::Pallet::::accepted_mrtd_values(); - ensure!(accepted_mrtd_values.contains(&mrtd_value), Error::::BadMrtdValue); - - // Check that the attestation public key is signed with the PCK - let provisioning_certification_key = decode_verifying_key( - &provisioning_certification_key - .to_vec() - .try_into() - .map_err(|_| Error::::CannotDecodeVerifyingKey)?, - ) - .map_err(|_| Error::::CannotDecodeVerifyingKey)?; + ensure!(accepted_mrtd_values.contains(&mrtd_value), VerifyQuoteError::BadMrtdValue); - quote - .verify_with_pck(provisioning_certification_key) - .map_err(|_| Error::::PckVerification)?; + let pck = verify_pck_certificate_chain("e)?; PendingAttestations::::remove(attestee); // TODO #982 If anything fails, don't just return an error - do something mean - Ok(()) + BoundedVec::try_from( + encode_verifying_key(&pck) + .map_err(|_| VerifyQuoteError::CannotEncodeVerifyingKey)? + .to_vec(), + ) + .map_err(|_| VerifyQuoteError::CannotEncodeVerifyingKey) } fn request_quote(who: &T::AccountId, nonce: [u8; 32]) { PendingAttestations::::insert(who, nonce) } } + + #[cfg(feature = "production")] + fn verify_pck_certificate_chain(quote: &Quote) -> Result { + quote.verify().map_err(|_| VerifyQuoteError::PckCertificateVerify) + } + + /// A mock version of verifying the PCK certificate chain. + /// When generating mock quotes, we just put the encoded PCK in place of the certificate chain + /// so this function just decodes it, checks it was used to sign the quote, and returns it + #[cfg(not(feature = "production"))] + fn verify_pck_certificate_chain(quote: &Quote) -> Result { + let provisioning_certification_key = + quote.pck_cert_chain().map_err(|_| VerifyQuoteError::PckCertificateNoCertificate)?; + let provisioning_certification_key = tdx_quote::decode_verifying_key( + &provisioning_certification_key + .try_into() + .map_err(|_| VerifyQuoteError::CannotDecodeVerifyingKey)?, + ) + .map_err(|_| VerifyQuoteError::CannotDecodeVerifyingKey)?; + + ensure!( + quote.verify_with_pck(&provisioning_certification_key).is_ok(), + VerifyQuoteError::PckCertificateVerify + ); + Ok(provisioning_certification_key) + } } diff --git a/pallets/attestation/src/mock.rs b/pallets/attestation/src/mock.rs index 7ed5c402f..575c2cead 100644 --- a/pallets/attestation/src/mock.rs +++ b/pallets/attestation/src/mock.rs @@ -316,7 +316,6 @@ impl pallet_staking_extension::Config for Test { type AttestationHandler = (); type Currency = Balances; type MaxEndpointLength = MaxEndpointLength; - type PckCertChainVerifier = pallet_staking_extension::pck::MockPckCertChainVerifier; type Randomness = TestPastRandomness; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/pallets/attestation/src/tests.rs b/pallets/attestation/src/tests.rs index 113d5cd15..7b57f452f 100644 --- a/pallets/attestation/src/tests.rs +++ b/pallets/attestation/src/tests.rs @@ -14,7 +14,7 @@ // along with this program. If not, see . use crate::mock::*; -use entropy_shared::{AttestationHandler, QuoteContext, QuoteInputData}; +use entropy_shared::{AttestationHandler, QuoteContext, QuoteInputData, VerifyQuoteError}; use frame_support::{assert_noop, assert_ok}; use rand_core::OsRng; @@ -40,11 +40,15 @@ fn verify_quote_works() { QuoteContext::Validate, ); - let quote = tdx_quote::Quote::mock(attestation_key.clone(), pck, input_data.0); + let quote = tdx_quote::Quote::mock( + attestation_key.clone(), + pck, + input_data.0, + pck_encoded.to_vec(), + ); assert_ok!(Attestation::verify_quote( &ATTESTEE, x25519_public_key, - sp_runtime::BoundedVec::try_from(pck_encoded.to_vec()).unwrap(), quote.as_bytes().to_vec(), QuoteContext::Validate, )); @@ -71,7 +75,12 @@ fn verify_quote_fails_with_mismatched_input_data() { QuoteContext::Validate, ); - let quote = tdx_quote::Quote::mock(attestation_key.clone(), pck, input_data.0); + let quote = tdx_quote::Quote::mock( + attestation_key.clone(), + pck.clone(), + input_data.0, + pck_encoded.to_vec(), + ); // We want to test that our quote verification fails if we commit to data that doesn't match // the `quote`. @@ -80,11 +89,10 @@ fn verify_quote_fails_with_mismatched_input_data() { Attestation::verify_quote( &mismatched_attestee, x25519_public_key, - sp_runtime::BoundedVec::try_from(pck_encoded.to_vec()).unwrap(), quote.as_bytes().to_vec(), QuoteContext::Validate, ), - crate::Error::::UnexpectedAttestation, + VerifyQuoteError::UnexpectedAttestation, ); // The X25519 public key we're comitting to here doesn't match what we used to generate the @@ -94,32 +102,10 @@ fn verify_quote_fails_with_mismatched_input_data() { Attestation::verify_quote( &ATTESTEE, mismatched_x25519_public_key, - sp_runtime::BoundedVec::try_from(pck_encoded.to_vec()).unwrap(), - quote.as_bytes().to_vec(), - QuoteContext::Validate, - ), - crate::Error::::IncorrectInputData, - ); - - // Here we have a random p256 ECDSA key which doesn't match the one used to generate the - // quote. - let mismatched_pck = [ - 34, 106, 242, 46, 148, 212, 105, 237, 205, 76, 112, 33, 10, 46, 94, 228, 46, 62, 226, - 127, 19, 73, 28, 106, 68, 253, 119, 138, 136, 246, 37, 251, - ]; - let mismatched_pck = tdx_quote::SigningKey::from_bytes(&mismatched_pck.into()).unwrap(); - let mismatched_pck_encoded = - tdx_quote::encode_verifying_key(mismatched_pck.verifying_key()).unwrap(); - - assert_noop!( - Attestation::verify_quote( - &ATTESTEE, - x25519_public_key, - sp_runtime::BoundedVec::try_from(mismatched_pck_encoded.to_vec()).unwrap(), quote.as_bytes().to_vec(), QuoteContext::Validate, ), - crate::Error::::PckVerification + VerifyQuoteError::IncorrectInputData, ); }) } diff --git a/pallets/staking/test_pck_certs/pck_cert.der b/pallets/attestation/test_pck_certs/pck_cert.der similarity index 100% rename from pallets/staking/test_pck_certs/pck_cert.der rename to pallets/attestation/test_pck_certs/pck_cert.der diff --git a/pallets/staking/test_pck_certs/platform_pcs_cert.der b/pallets/attestation/test_pck_certs/platform_pcs_cert.der similarity index 100% rename from pallets/staking/test_pck_certs/platform_pcs_cert.der rename to pallets/attestation/test_pck_certs/platform_pcs_cert.der diff --git a/pallets/propagation/src/mock.rs b/pallets/propagation/src/mock.rs index e3d3bf77e..fac31d4ec 100644 --- a/pallets/propagation/src/mock.rs +++ b/pallets/propagation/src/mock.rs @@ -311,7 +311,6 @@ impl pallet_staking_extension::Config for Test { type AttestationHandler = (); type Currency = Balances; type MaxEndpointLength = MaxEndpointLength; - type PckCertChainVerifier = pallet_staking_extension::pck::MockPckCertChainVerifier; type Randomness = TestPastRandomness; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/pallets/registry/src/mock.rs b/pallets/registry/src/mock.rs index 573ea9e75..c38c485da 100644 --- a/pallets/registry/src/mock.rs +++ b/pallets/registry/src/mock.rs @@ -308,7 +308,6 @@ impl pallet_staking_extension::Config for Test { type AttestationHandler = (); type Currency = Balances; type MaxEndpointLength = MaxEndpointLength; - type PckCertChainVerifier = pallet_staking_extension::pck::MockPckCertChainVerifier; type Randomness = TestPastRandomness; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/pallets/staking/Cargo.toml b/pallets/staking/Cargo.toml index ff6ecfbbe..04cb6a592 100644 --- a/pallets/staking/Cargo.toml +++ b/pallets/staking/Cargo.toml @@ -28,9 +28,6 @@ sp-runtime ={ version="32.0.0", default-features=false } sp-staking ={ version="27.0.0", default-features=false } sp-std ={ version="14.0.0", default-features=false } sp-consensus-babe ={ version="0.33.0", default-features=false } -x509-verify ={ version="0.4.6", features=["x509"] } -spki ="0.7.3" -p256 ={ version="0.13.2", default-features=false, features=["ecdsa"] } rand ={ version="0.8.5", default-features=false, features=["alloc"] } pallet-parameters={ version="0.3.0", path="../parameters", default-features=false } @@ -38,10 +35,9 @@ pallet-slashing={ version="0.3.0", path="../slashing", default-features=false } entropy-shared={ version="0.3.0", path="../../crates/shared", features=[ "wasm-no-std", ], default-features=false } - -# We only use this in benchmarks and tests. Since the benches don't run with the `test` feature we need to -# import it here _and_ as a `dev-dependency`. -tdx-quote={ version="0.0.1", features=["mock"], optional=true } +tdx-quote={ git="https://github.com/entropyxyz/tdx-quote.git", rev="67a9d011809d0c9109d1ac42aeb809a84b663be6", features=[ + "mock", +], optional=true } [dev-dependencies] frame-election-provider-support={ version="29.0.0", default-features=false } @@ -53,11 +49,10 @@ sp-io ={ version="31.0.0", default-features=false } sp-npos-elections ={ version="27.0.0", default-features=false } rand_core="0.6.4" -tdx-quote={ version="0.0.1", features=["mock"] } [features] default=['std'] -runtime-benchmarks=['frame-benchmarking', 'tdx-quote'] +runtime-benchmarks=['frame-benchmarking', "tdx-quote"] std=[ "sp-npos-elections/std", "sp-staking/std", diff --git a/pallets/staking/src/benchmarking.rs b/pallets/staking/src/benchmarking.rs index a6dad4dae..8b59b3e0a 100644 --- a/pallets/staking/src/benchmarking.rs +++ b/pallets/staking/src/benchmarking.rs @@ -16,7 +16,6 @@ //! Benchmarking setup for pallet-propgation #![allow(unused_imports)] use super::*; -use crate::pck::{signing_key_from_seed, MOCK_PCK_DERIVED_FROM_NULL_ARRAY}; #[allow(unused_imports)] use crate::Pallet as Staking; use entropy_shared::{AttestationHandler, QuoteContext, MAX_SIGNERS}; @@ -34,8 +33,14 @@ use pallet_staking::{ Event as FrameStakingEvent, MaxNominationsOf, MaxValidatorsCount, Nominations, Pallet as FrameStaking, RewardDestination, ValidatorPrefs, }; +use rand::{rngs::StdRng, SeedableRng}; use sp_std::{vec, vec::Vec}; +use tdx_quote::SigningKey; +const MOCK_PCK_DERIVED_FROM_NULL_ARRAY: [u8; 33] = [ + 3, 237, 193, 27, 177, 204, 234, 67, 54, 141, 157, 13, 62, 87, 113, 224, 4, 121, 206, 251, 190, + 151, 134, 87, 68, 46, 37, 163, 127, 97, 252, 174, 108, +]; const NULL_ARR: [u8; 32] = [0; 32]; const SEED: u32 = 0; @@ -89,6 +94,7 @@ fn prepare_attestation_for_validate( threshold: T::AccountId, x25519_public_key: [u8; 32], endpoint: Vec, + quote_context: QuoteContext, ) -> (Vec, JoiningServerInfo) { let nonce = NULL_ARR; let quote = { @@ -105,20 +111,16 @@ fn prepare_attestation_for_validate( &threshold, x25519_public_key, nonce, - QuoteContext::Validate, + quote_context, ); - - tdx_quote::Quote::mock(attestation_key.clone(), pck, input_data.0).as_bytes().to_vec() + let pck_encoded = tdx_quote::encode_verifying_key(pck.verifying_key()).unwrap(); + tdx_quote::Quote::mock(attestation_key.clone(), pck, input_data.0, pck_encoded.to_vec()) + .as_bytes() + .to_vec() }; - let joining_server_info = JoiningServerInfo { - tss_account: threshold.clone(), - x25519_public_key, - endpoint, - // Since we are using the mock PckCertChainVerifier, this needs to be the same seed for - // generating the PCK as we used to sign the quote above - pck_certificate_chain: vec![NULL_ARR.to_vec()], - }; + let joining_server_info = + JoiningServerInfo { tss_account: threshold.clone(), x25519_public_key, endpoint }; // We need to tell the attestation handler that we want a quote. This will let the system to // know to expect one back when we call `validate()`. @@ -126,6 +128,11 @@ fn prepare_attestation_for_validate( (quote, joining_server_info) } +fn signing_key_from_seed(input: [u8; 32]) -> SigningKey { + let mut pck_seeder = StdRng::from_seed(input); + SigningKey::random(&mut pck_seeder) +} + fn prep_bond_and_validate( validate_also: bool, caller: T::AccountId, @@ -147,8 +154,12 @@ fn prep_bond_and_validate( if validate_also { let endpoint = b"http://localhost:3001".to_vec(); - let (quote, joining_server_info) = - prepare_attestation_for_validate::(threshold, x25519_public_key, endpoint); + let (quote, joining_server_info) = prepare_attestation_for_validate::( + threshold, + x25519_public_key, + endpoint, + QuoteContext::Validate, + ); assert_ok!(>::validate( RawOrigin::Signed(bonder.clone()).into(), @@ -198,6 +209,7 @@ benchmarks! { threshold, x25519_public_key, endpoint.clone().to_vec(), + QuoteContext::ChangeEndpoint, ) .0; }: _(RawOrigin::Signed(bonder.clone()), endpoint.to_vec(), quote) @@ -239,17 +251,15 @@ benchmarks! { new_threshold.clone(), x25519_public_key, endpoint.clone().to_vec(), + QuoteContext::ChangeThresholdAccounts, ); - let pck_certificate_chain = joining_server_info.pck_certificate_chain; - let signers = vec![validator_id_signers.clone(); s as usize]; Signers::::put(signers.clone()); }: _( RawOrigin::Signed(_bonder.clone()), new_threshold.clone(), x25519_public_key.clone(), - pck_certificate_chain, quote ) verify { @@ -388,7 +398,7 @@ benchmarks! { ); let (quote, joining_server_info) = - prepare_attestation_for_validate::(threshold_account.clone(), x25519_public_key, endpoint.clone()); + prepare_attestation_for_validate::(threshold_account.clone(), x25519_public_key, endpoint.clone(), QuoteContext::Validate); }: _(RawOrigin::Signed(bonder.clone()), ValidatorPrefs::default(), joining_server_info, quote) verify { assert_last_event::( diff --git a/pallets/staking/src/lib.rs b/pallets/staking/src/lib.rs index b4961cabe..5181e3307 100644 --- a/pallets/staking/src/lib.rs +++ b/pallets/staking/src/lib.rs @@ -41,8 +41,6 @@ use serde::{Deserialize, Serialize}; pub use crate::weights::WeightInfo; -pub mod pck; - #[cfg(test)] mod mock; @@ -60,7 +58,7 @@ use sp_staking::SessionIndex; #[frame_support::pallet] pub mod pallet { use entropy_shared::{ - QuoteContext, ValidatorInfo, X25519PublicKey, MAX_SIGNERS, + QuoteContext, ValidatorInfo, VerifyQuoteError, X25519PublicKey, MAX_SIGNERS, PREGENERATED_NETWORK_VERIFYING_KEY, TEST_RESHARE_BLOCK_NUMBER, VERIFICATION_KEY_LENGTH, }; use frame_support::{ @@ -70,7 +68,6 @@ pub mod pallet { DefaultNoBound, }; use frame_system::pallet_prelude::*; - use pck::PckCertChainVerifier; use rand_chacha::{ rand_core::{RngCore, SeedableRng}, ChaCha20Rng, ChaChaRng, @@ -98,9 +95,6 @@ pub mod pallet { /// The weight information of this pallet. type WeightInfo: WeightInfo; - /// A type that verifies a provisioning certification key (PCK) certificate chain. - type PckCertChainVerifier: PckCertChainVerifier; - /// Something that provides randomness in the runtime. type Randomness: Randomness>; @@ -133,14 +127,13 @@ pub mod pallet { } /// Information about a threshold server in the process of joining - /// This becomes a [ServerInfo] when the Pck certificate chain has been validated + /// This becomes a [ServerInfo] when an attestation has been verified #[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct JoiningServerInfo { pub tss_account: AccountId, pub x25519_public_key: X25519PublicKey, pub endpoint: TssServerURL, - pub pck_certificate_chain: Vec>, } /// Info that is requiered to do a proactive refresh @@ -347,20 +340,48 @@ pub mod pallet { NoUnnominatingWhenSigner, NoUnnominatingWhenNextSigner, NoChangingThresholdAccountWhenSigner, + /// Quote could not be parsed or verified + BadQuote, + /// Attestation extrinsic submitted when not requested + UnexpectedAttestation, + /// Hashed input data does not match what was expected + IncorrectInputData, + /// Unacceptable VM image running + BadMrtdValue, + /// Cannot encode verifying key (PCK) + CannotEncodeVerifyingKey, + /// Cannot decode verifying key (PCK) + CannotDecodeVerifyingKey, + /// PCK certificate chain cannot be parsed PckCertificateParse, + /// PCK certificate chain cannot be verified PckCertificateVerify, + /// PCK certificate chain public key is not well formed PckCertificateBadPublicKey, + /// Pck certificate could not be extracted from quote PckCertificateNoCertificate, - FailedAttestationCheck, } - impl From for Error { - fn from(error: pck::PckParseVerifyError) -> Self { + impl From for Error { + /// As there are many reasons why quote verification can fail we want these error types to + /// be reflected in the dispatch errors from extrinsics in this pallet which do quote + /// verification + fn from(error: VerifyQuoteError) -> Self { match error { - pck::PckParseVerifyError::Parse => Error::::PckCertificateParse, - pck::PckParseVerifyError::Verify => Error::::PckCertificateVerify, - pck::PckParseVerifyError::BadPublicKey => Error::::PckCertificateBadPublicKey, - pck::PckParseVerifyError::NoCertificate => Error::::PckCertificateNoCertificate, + VerifyQuoteError::BadQuote => Error::::BadQuote, + VerifyQuoteError::UnexpectedAttestation => Error::::UnexpectedAttestation, + VerifyQuoteError::IncorrectInputData => Error::::IncorrectInputData, + VerifyQuoteError::BadMrtdValue => Error::::BadMrtdValue, + VerifyQuoteError::CannotEncodeVerifyingKey => Error::::CannotEncodeVerifyingKey, + VerifyQuoteError::PckCertificateParse => Error::::PckCertificateParse, + VerifyQuoteError::PckCertificateVerify => Error::::PckCertificateVerify, + VerifyQuoteError::PckCertificateBadPublicKey => { + Error::::PckCertificateBadPublicKey + }, + VerifyQuoteError::PckCertificateNoCertificate => { + Error::::PckCertificateNoCertificate + }, + VerifyQuoteError::CannotDecodeVerifyingKey => Error::::CannotDecodeVerifyingKey, } } } @@ -430,17 +451,12 @@ pub mod pallet { if let Some(server_info) = maybe_server_info { // Before we modify the `server_info`, we want to check that the validator is // still running TDX hardware. - ensure!( - >::verify_quote( - &server_info.tss_account.clone(), - server_info.x25519_public_key, - server_info.provisioning_certification_key.clone(), - quote, - QuoteContext::ChangeEndpoint, - ) - .is_ok(), - Error::::FailedAttestationCheck - ); + >::verify_quote( + &server_info.tss_account.clone(), + server_info.x25519_public_key, + quote, + QuoteContext::ChangeEndpoint, + )?; server_info.endpoint.clone_from(&endpoint); @@ -473,7 +489,6 @@ pub mod pallet { origin: OriginFor, tss_account: T::AccountId, x25519_public_key: X25519PublicKey, - pck_certificate_chain: Vec>, quote: Vec, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; @@ -493,30 +508,19 @@ pub mod pallet { Error::::NoChangingThresholdAccountWhenSigner ); - let provisioning_certification_key = - T::PckCertChainVerifier::verify_pck_certificate_chain(pck_certificate_chain) - .map_err(|error| { - let e: Error = error.into(); - e - })?; - let new_server_info: ServerInfo = ThresholdServers::::try_mutate( &validator_id, |maybe_server_info| { if let Some(server_info) = maybe_server_info { // Before we modify the `server_info`, we want to check that the validator is // still running TDX hardware. - ensure!( + let provisioning_certification_key = >::verify_quote( &tss_account.clone(), x25519_public_key, - provisioning_certification_key.clone(), quote, QuoteContext::ChangeThresholdAccounts, - ) - .is_ok(), - Error::::FailedAttestationCheck - ); + )?; server_info.tss_account = tss_account; server_info.x25519_public_key = x25519_public_key; @@ -635,42 +639,31 @@ pub mod pallet { ) -> DispatchResult { let who = ensure_signed(origin.clone())?; - let provisioning_certification_key = - T::PckCertChainVerifier::verify_pck_certificate_chain( - joining_server_info.pck_certificate_chain, - ) - .map_err(|error| { - let e: Error = error.into(); - e - })?; - - let server_info = ServerInfo:: { - tss_account: joining_server_info.tss_account, - x25519_public_key: joining_server_info.x25519_public_key, - endpoint: joining_server_info.endpoint, - provisioning_certification_key, - }; ensure!( - server_info.endpoint.len() as u32 <= T::MaxEndpointLength::get(), + joining_server_info.endpoint.len() as u32 <= T::MaxEndpointLength::get(), Error::::EndpointTooLong ); ensure!( - !ThresholdToStash::::contains_key(&server_info.tss_account), + !ThresholdToStash::::contains_key(&joining_server_info.tss_account), Error::::TssAccountAlreadyExists ); - ensure!( + let provisioning_certification_key = >::verify_quote( - &server_info.tss_account.clone(), - server_info.x25519_public_key, - server_info.provisioning_certification_key.clone(), + &joining_server_info.tss_account.clone(), + joining_server_info.x25519_public_key, quote, QuoteContext::Validate, ) - .is_ok(), - Error::::FailedAttestationCheck - ); + .map_err(>>::into)?; + + let server_info = ServerInfo:: { + tss_account: joining_server_info.tss_account, + x25519_public_key: joining_server_info.x25519_public_key, + endpoint: joining_server_info.endpoint, + provisioning_certification_key, + }; pallet_staking::Pallet::::validate(origin, prefs)?; diff --git a/pallets/staking/src/mock.rs b/pallets/staking/src/mock.rs index 4c71b12b8..bd6faffef 100644 --- a/pallets/staking/src/mock.rs +++ b/pallets/staking/src/mock.rs @@ -38,7 +38,6 @@ use sp_staking::{EraIndex, SessionIndex}; use sp_std::vec; use crate as pallet_staking_extension; -use pallet_staking_extension::pck::MockPckCertChainVerifier; type Block = frame_system::mocking::MockBlock; type BlockNumber = u64; @@ -400,18 +399,18 @@ impl entropy_shared::AttestationHandler for MockAttestationHandler { fn verify_quote( _attestee: &AccountId, _x25519_public_key: entropy_shared::X25519PublicKey, - _provisioning_certification_key: entropy_shared::BoundedVecEncodedVerifyingKey, quote: Vec, _context: QuoteContext, - ) -> Result<(), sp_runtime::DispatchError> { + ) -> Result + { let quote: Result<[u8; 32], _> = quote.try_into(); match quote { - Ok(q) if q == VALID_QUOTE => Ok(()), - Ok(q) if q == INVALID_QUOTE => Err(sp_runtime::DispatchError::Other("Invalid quote")), + Ok(q) if q == VALID_QUOTE => Ok([0; 33].to_vec().try_into().unwrap()), + Ok(q) if q == INVALID_QUOTE => Err(entropy_shared::VerifyQuoteError::BadQuote), _ => { // We don't really want to verify quotes for tests in this pallet, so if we get // something else we'll just accept it. - Ok(()) + Ok(BoundedVec::new()) }, } } @@ -460,7 +459,6 @@ impl pallet_staking_extension::Config for Test { type AttestationHandler = MockAttestationHandler; type Currency = Balances; type MaxEndpointLength = MaxEndpointLength; - type PckCertChainVerifier = MockPckCertChainVerifier; type Randomness = TestPastRandomness; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); diff --git a/pallets/staking/src/pck/Intel_SGX_Provisioning_Certification_RootCA.cer b/pallets/staking/src/pck/Intel_SGX_Provisioning_Certification_RootCA.cer deleted file mode 100644 index 768806c673709fb71537020abbabad2b1dc23773..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 659 zcmXqLV(K?&Vlr94%*4pVB%+kcb1iDFN$2P0EmMQ`2S{70v#l}UV&l+i^EhYA!pvll zVJKxF!NwfQ!ptM+nOBmUqY&&Kp%9dxU!vgb2$SQ2$vNj2733EsmSpDV8HyMPf%LKS za0e&ml_V-S=Oh*-8gd(OfM-Ch~kvMtrKjmv1bZ!Z(*nQf5%loS+O>FXz_7L{bCWhN(hi5#{Wtmy%j!APX{+k420HyOn$(X?{k= z|17|0XJi9Mo~$s4&tbp@q?i~PkrM{9Cxd|-lOn^@{ZBasf9gARD%>&aGSGW`QSWG5 z{WqmIDbv3SH3&@mV{?50sw!g B#yS81 diff --git a/pallets/staking/src/pck/mock.rs b/pallets/staking/src/pck/mock.rs deleted file mode 100644 index 5623cdd0d..000000000 --- a/pallets/staking/src/pck/mock.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2023 Entropy Cryptography Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -use super::{CompressedVerifyingKey, PckCertChainVerifier, PckParseVerifyError}; -use p256::ecdsa::{SigningKey, VerifyingKey}; -use rand::{rngs::StdRng, SeedableRng}; -use sp_std::vec::Vec; - -/// This is used in the benchmarking tests to check that ServerInfo is as expected -pub const MOCK_PCK_DERIVED_FROM_NULL_ARRAY: [u8; 33] = [ - 3, 237, 193, 27, 177, 204, 234, 67, 54, 141, 157, 13, 62, 87, 113, 224, 4, 121, 206, 251, 190, - 151, 134, 87, 68, 46, 37, 163, 127, 97, 252, 174, 108, -]; - -/// A PCK certificate chain verifier for testing. -/// Rather than actually use test certificates, we give here the TSS account ID instead of the first -/// certificate, and derive a keypair from it. The same keypair will be derived when creating a mock -/// quote in entropy-tss -pub struct MockPckCertChainVerifier {} - -impl PckCertChainVerifier for MockPckCertChainVerifier { - fn verify_pck_certificate_chain( - pck_certificate_chain: Vec>, - ) -> Result { - let first_certificate = - pck_certificate_chain.first().ok_or(PckParseVerifyError::NoCertificate)?; - - // Read the certificate bytes as a TSS account id - let tss_account_id: [u8; 32] = - first_certificate.clone().try_into().map_err(|_| PckParseVerifyError::Parse)?; - - // Derive a keypair - let pck_secret = signing_key_from_seed(tss_account_id); - - // Convert/compress the public key - let pck_public = VerifyingKey::from(&pck_secret); - let pck_public = pck_public.to_encoded_point(true).as_bytes().to_vec(); - pck_public.try_into().map_err(|_| PckParseVerifyError::Parse) - } -} - -pub fn signing_key_from_seed(input: [u8; 32]) -> SigningKey { - let mut pck_seeder = StdRng::from_seed(input); - SigningKey::random(&mut pck_seeder) -} diff --git a/pallets/staking/src/pck/mod.rs b/pallets/staking/src/pck/mod.rs deleted file mode 100644 index 3694d988a..000000000 --- a/pallets/staking/src/pck/mod.rs +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2023 Entropy Cryptography Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -mod mock; -pub use mock::{signing_key_from_seed, MockPckCertChainVerifier, MOCK_PCK_DERIVED_FROM_NULL_ARRAY}; -mod production; -use super::VerifyingKey as CompressedVerifyingKey; -use core::array::TryFromSliceError; -use sp_std::vec::Vec; - -/// Provides a way of verifying a chain of certificates to give a chain of trust between the -/// provisioning certification key used to sign a TDX quote to the Intel route certificate authority -pub trait PckCertChainVerifier { - /// Verify an arbitrary chain of DER-encoded x509 certificates against Intel's root CA. - /// Typically this is two certificates, the PCK certificate and an intermediary provider - /// certificate - fn verify_pck_certificate_chain( - pck_certificate_chain: Vec>, - ) -> Result; -} - -/// An error when parsing or verifying a PCK or provider certificate -#[derive(Debug)] -pub enum PckParseVerifyError { - Parse, - Verify, - BadPublicKey, - NoCertificate, -} - -impl From for PckParseVerifyError { - fn from(_: spki::der::Error) -> PckParseVerifyError { - PckParseVerifyError::Parse - } -} - -impl From for PckParseVerifyError { - fn from(_: x509_verify::Error) -> PckParseVerifyError { - PckParseVerifyError::Verify - } -} - -impl From for PckParseVerifyError { - fn from(_: TryFromSliceError) -> PckParseVerifyError { - PckParseVerifyError::BadPublicKey - } -} diff --git a/pallets/staking/src/pck/production.rs b/pallets/staking/src/pck/production.rs deleted file mode 100644 index 7a0404123..000000000 --- a/pallets/staking/src/pck/production.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (C) 2023 Entropy Cryptography Inc. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . -use sp_std::vec::Vec; -use x509_verify::{ - der::{Decode, Encode}, - x509_cert::Certificate, - Signature, VerifyInfo, VerifyingKey, -}; - -use super::{CompressedVerifyingKey, PckCertChainVerifier, PckParseVerifyError}; - -/// Intels root CA certificate in DER format available from here: -/// https://certificates.trustedservices.intel.com/Intel_SGX_Provisioning_Certification_RootCA.cer -/// Valid until December 31 2049 -const INTEL_ROOT_CA_DER: &[u8; 659] = - include_bytes!("Intel_SGX_Provisioning_Certification_RootCA.cer"); - -/// A PCK certificate chain verifier for use in production where entropy-tss is running on TDX -/// hardware and we have a PCK certificate chain -pub struct ProductionPckCertChainVerifier {} - -impl PckCertChainVerifier for ProductionPckCertChainVerifier { - fn verify_pck_certificate_chain( - pck_certificate_chain: Vec>, - ) -> Result { - let pck_uncompressed = verify_pck_cert_chain(pck_certificate_chain)?; - - // Compress / convert public key - let point = p256::EncodedPoint::from_bytes(pck_uncompressed) - .map_err(|_| PckParseVerifyError::BadPublicKey)?; - let pck_verifying_key = p256::ecdsa::VerifyingKey::from_encoded_point(&point) - .map_err(|_| PckParseVerifyError::BadPublicKey)?; - let pck_compressed = pck_verifying_key.to_encoded_point(true).as_bytes().to_vec(); - pck_compressed.try_into().map_err(|_| PckParseVerifyError::BadPublicKey) - } -} - -/// Validate PCK and provider certificates and if valid return the PCK -/// These certificates will be provided by a joining validator -fn verify_pck_cert_chain(certificates_der: Vec>) -> Result<[u8; 65], PckParseVerifyError> { - if certificates_der.is_empty() { - return Err(PckParseVerifyError::NoCertificate); - } - - // Parse the certificates - let mut certificates = Vec::new(); - for certificate in certificates_der { - certificates.push(Certificate::from_der(&certificate)?); - } - // Add the root certificate to the end of the chain. Since the root cert is self-signed, this - // will work regardless of whether the user has included this certicate in the chain or not - certificates.push(Certificate::from_der(INTEL_ROOT_CA_DER)?); - - // Verify the certificate chain - for i in 0..certificates.len() { - let verifying_key: &VerifyingKey = if i + 1 == certificates.len() { - &certificates[i].tbs_certificate.subject_public_key_info.clone().try_into()? - } else { - &certificates[i + 1].tbs_certificate.subject_public_key_info.clone().try_into()? - }; - verify_cert(&certificates[i], verifying_key)?; - } - - // Get the first certificate - let pck_key = &certificates - .first() - .ok_or(PckParseVerifyError::NoCertificate)? - .tbs_certificate - .subject_public_key_info - .subject_public_key; - - Ok(pck_key.as_bytes().ok_or(PckParseVerifyError::BadPublicKey)?.try_into()?) -} - -/// Given a cerificate and a public key, verify the certificate -fn verify_cert(subject: &Certificate, issuer_pk: &VerifyingKey) -> Result<(), PckParseVerifyError> { - let verify_info = VerifyInfo::new( - subject.tbs_certificate.to_der()?.into(), - Signature::new( - &subject.signature_algorithm, - subject.signature.as_bytes().ok_or(PckParseVerifyError::Parse)?, - ), - ); - Ok(issuer_pk.verify(&verify_info)?) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_verify_pck_cert_chain() { - let pck = include_bytes!("../../test_pck_certs/pck_cert.der").to_vec(); - let platform = include_bytes!("../../test_pck_certs/platform_pcs_cert.der").to_vec(); - assert!(ProductionPckCertChainVerifier::verify_pck_certificate_chain(vec![pck, platform]) - .is_ok()); - } -} diff --git a/pallets/staking/src/tests.rs b/pallets/staking/src/tests.rs index f2a957b85..54074d087 100644 --- a/pallets/staking/src/tests.rs +++ b/pallets/staking/src/tests.rs @@ -14,8 +14,8 @@ // along with this program. If not, see . use crate::{ - mock::*, pck::MOCK_PCK_DERIVED_FROM_NULL_ARRAY, tests::RuntimeEvent, Error, JoiningServerInfo, - NextSignerInfo, NextSigners, ServerInfo, Signers, + mock::*, tests::RuntimeEvent, Error, JoiningServerInfo, NextSignerInfo, NextSigners, + ServerInfo, Signers, }; use codec::Encode; use frame_support::{assert_noop, assert_ok}; @@ -61,12 +61,8 @@ fn it_takes_in_an_endpoint() { pallet_staking::RewardDestination::Account(1), )); - let joining_server_info = JoiningServerInfo { - tss_account: 3, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: vec![[0u8; 32].to_vec()], - }; + let joining_server_info = + JoiningServerInfo { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: vec![20] }; assert_ok!(Staking::validate( RuntimeOrigin::signed(1), pallet_staking::ValidatorPrefs::default(), @@ -83,7 +79,6 @@ fn it_takes_in_an_endpoint() { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: [20; (crate::tests::MaxEndpointLength::get() + 1) as usize].to_vec(), - pck_certificate_chain: vec![[0u8; 32].to_vec()], }; assert_noop!( Staking::validate( @@ -99,7 +94,6 @@ fn it_takes_in_an_endpoint() { tss_account: 5, x25519_public_key: NULL_ARR, endpoint: vec![20, 20], - pck_certificate_chain: vec![[0u8; 32].to_vec()], }; assert_noop!( Staking::validate( @@ -122,12 +116,8 @@ fn it_will_not_allow_validator_to_use_existing_tss_account() { pallet_staking::RewardDestination::Account(1), )); - let joining_server_info = JoiningServerInfo { - tss_account: 3, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: vec![[0u8; 32].to_vec()], - }; + let joining_server_info = + JoiningServerInfo { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: vec![20] }; assert_ok!(Staking::validate( RuntimeOrigin::signed(1), pallet_staking::ValidatorPrefs::default(), @@ -168,7 +158,6 @@ fn it_changes_endpoint() { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: endpoint.clone(), - pck_certificate_chain: vec![[0u8; 32].to_vec()], }; assert_ok!(Staking::validate( RuntimeOrigin::signed(1), @@ -206,7 +195,6 @@ fn it_doesnt_change_endpoint_with_invalid_quote() { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: endpoint.clone(), - pck_certificate_chain: vec![[0u8; 32].to_vec()], }; assert_ok!(Staking::validate( @@ -218,7 +206,7 @@ fn it_doesnt_change_endpoint_with_invalid_quote() { assert_noop!( Staking::change_endpoint(RuntimeOrigin::signed(1), endpoint, INVALID_QUOTE.to_vec()), - Error::::FailedAttestationCheck + Error::::BadQuote ); }) } @@ -232,13 +220,8 @@ fn it_changes_threshold_account() { pallet_staking::RewardDestination::Account(1), )); - let pck_certificate_chain = vec![vec![0u8; 32]]; - let joining_server_info = JoiningServerInfo { - tss_account: 3, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: pck_certificate_chain.clone(), - }; + let joining_server_info = + JoiningServerInfo { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: vec![20] }; assert_ok!(Staking::validate( RuntimeOrigin::signed(1), pallet_staking::ValidatorPrefs::default(), @@ -250,7 +233,6 @@ fn it_changes_threshold_account() { RuntimeOrigin::signed(1), 4, NULL_ARR, - pck_certificate_chain.clone(), VALID_QUOTE.to_vec() )); assert_eq!(Staking::threshold_server(1).unwrap().tss_account, 4); @@ -261,7 +243,6 @@ fn it_changes_threshold_account() { RuntimeOrigin::signed(4), 5, NULL_ARR, - pck_certificate_chain.clone(), VALID_QUOTE.to_vec() ), Error::::NotController @@ -274,12 +255,8 @@ fn it_changes_threshold_account() { pallet_staking::RewardDestination::Account(2), )); - let joining_server_info = JoiningServerInfo { - tss_account: 5, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: pck_certificate_chain.clone(), - }; + let joining_server_info = + JoiningServerInfo { tss_account: 5, x25519_public_key: NULL_ARR, endpoint: vec![20] }; assert_ok!(Staking::validate( RuntimeOrigin::signed(2), pallet_staking::ValidatorPrefs::default(), @@ -292,7 +269,6 @@ fn it_changes_threshold_account() { RuntimeOrigin::signed(1), 5, NULL_ARR, - pck_certificate_chain.clone(), VALID_QUOTE.to_vec() ), Error::::TssAccountAlreadyExists @@ -304,7 +280,6 @@ fn it_changes_threshold_account() { RuntimeOrigin::signed(1), 9, NULL_ARR, - pck_certificate_chain.clone(), VALID_QUOTE.to_vec() ), Error::::NoChangingThresholdAccountWhenSigner @@ -321,13 +296,8 @@ fn it_doesnt_allow_changing_threshold_account_with_invalid_quote() { pallet_staking::RewardDestination::Account(1), )); - let pck_certificate_chain = vec![[0u8; 32].to_vec()]; - let joining_server_info = JoiningServerInfo { - tss_account: 3, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: pck_certificate_chain.clone(), - }; + let joining_server_info = + JoiningServerInfo { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: vec![20] }; assert_ok!(Staking::validate( RuntimeOrigin::signed(1), pallet_staking::ValidatorPrefs::default(), @@ -340,10 +310,9 @@ fn it_doesnt_allow_changing_threshold_account_with_invalid_quote() { RuntimeOrigin::signed(1), 4, NULL_ARR, - pck_certificate_chain.clone(), INVALID_QUOTE.to_vec() ), - Error::::FailedAttestationCheck + Error::::BadQuote ); }) } @@ -357,13 +326,8 @@ fn it_will_not_allow_existing_tss_account_when_changing_threshold_account() { pallet_staking::RewardDestination::Account(1), )); - let pck_certificate_chain = vec![[0u8; 32].to_vec()]; - let joining_server_info = JoiningServerInfo { - tss_account: 3, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: pck_certificate_chain.clone(), - }; + let joining_server_info = + JoiningServerInfo { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: vec![20] }; assert_ok!(Staking::validate( RuntimeOrigin::signed(1), pallet_staking::ValidatorPrefs::default(), @@ -378,12 +342,8 @@ fn it_will_not_allow_existing_tss_account_when_changing_threshold_account() { pallet_staking::RewardDestination::Account(2), )); - let joining_server_info = JoiningServerInfo { - tss_account: 5, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: pck_certificate_chain.clone(), - }; + let joining_server_info = + JoiningServerInfo { tss_account: 5, x25519_public_key: NULL_ARR, endpoint: vec![20] }; assert_ok!(Staking::validate( RuntimeOrigin::signed(2), pallet_staking::ValidatorPrefs::default(), @@ -396,7 +356,6 @@ fn it_will_not_allow_existing_tss_account_when_changing_threshold_account() { RuntimeOrigin::signed(1), 5, NULL_ARR, - pck_certificate_chain.clone(), VALID_QUOTE.to_vec() ), Error::::TssAccountAlreadyExists @@ -416,12 +375,8 @@ fn it_deletes_when_no_bond_left() { pallet_staking::RewardDestination::Account(1), )); - let joining_server_info = JoiningServerInfo { - tss_account: 3, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: vec![[0u8; 32].to_vec()], - }; + let joining_server_info = + JoiningServerInfo { tss_account: 3, x25519_public_key: NULL_ARR, endpoint: vec![20] }; assert_ok!(Staking::validate( RuntimeOrigin::signed(2), pallet_staking::ValidatorPrefs::default(), @@ -702,12 +657,8 @@ fn it_requires_attestation_before_validate_is_succesful() { pallet_staking::RewardDestination::Account(alice), )); - let joining_server_info = JoiningServerInfo { - tss_account: bob, - x25519_public_key: NULL_ARR, - endpoint: vec![20], - pck_certificate_chain: vec![[0u8; 32].to_vec()], - }; + let joining_server_info = + JoiningServerInfo { tss_account: bob, x25519_public_key: NULL_ARR, endpoint: vec![20] }; // First we test that an invalid attestation doesn't allow us to submit our candidacy. assert_noop!( @@ -717,7 +668,7 @@ fn it_requires_attestation_before_validate_is_succesful() { joining_server_info.clone(), INVALID_QUOTE.to_vec(), ), - Error::::FailedAttestationCheck + Error::::BadQuote ); assert_eq!(Staking::threshold_server(bob), None); @@ -735,10 +686,7 @@ fn it_requires_attestation_before_validate_is_succesful() { tss_account: joining_server_info.tss_account, x25519_public_key: joining_server_info.x25519_public_key, endpoint: joining_server_info.endpoint, - provisioning_certification_key: MOCK_PCK_DERIVED_FROM_NULL_ARRAY - .to_vec() - .try_into() - .unwrap(), + provisioning_certification_key: [0; 33].to_vec().try_into().unwrap(), }; assert_eq!(Staking::threshold_to_stash(bob), Some(alice)); assert_eq!(Staking::threshold_server(alice), Some(server_info)); diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 3cdcdb26b..ec9553a2e 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -273,3 +273,6 @@ try-runtime=[ "pallet-utility/try-runtime", "pallet-vesting/try-runtime", ] +# Enables real PCK certificate chain verification - which means TSS nodes must be runnning on TDX +# hardware +production=["pallet-attestation/production"] diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index 461a4d49d..874ee062b 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -718,7 +718,6 @@ impl pallet_staking_extension::Config for Runtime { type AttestationHandler = Attestation; type Currency = Balances; type MaxEndpointLength = MaxEndpointLength; - type PckCertChainVerifier = pallet_staking_extension::pck::MockPckCertChainVerifier; type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_staking_extension::WeightInfo;