Skip to content

Commit

Permalink
Extract PCK certificate chain from quotes (#1209)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>
  • Loading branch information
ameba23 and HCastano authored Dec 18, 2024
1 parent 147fdf4 commit 664e406
Show file tree
Hide file tree
Showing 33 changed files with 326 additions and 541 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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
Expand All @@ -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

Expand Down
20 changes: 14 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions crates/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
Binary file modified crates/client/entropy_metadata.scale
Binary file not shown.
4 changes: 0 additions & 4 deletions crates/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,6 @@ pub async fn get_quote_and_change_threshold_accounts(
validator_keypair: sr25519::Pair,
new_tss_account: SubxtAccountId32,
new_x25519_public_key: [u8; 32],
new_pck_certificate_chain: Vec<Vec<u8>>,
) -> Result<ThresholdAccountChanged, ClientError> {
let quote = get_tdx_quote_with_validator_id(
api,
Expand All @@ -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
Expand All @@ -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<Vec<u8>>,
quote: Vec<u8>,
) -> Result<ThresholdAccountChanged, ClientError> {
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(
Expand Down
19 changes: 11 additions & 8 deletions crates/client/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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
Expand Down
37 changes: 32 additions & 5 deletions crates/shared/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,14 @@ impl std::fmt::Display for QuoteContext {
#[cfg(not(feature = "wasm"))]
pub trait AttestationHandler<AccountId> {
/// 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<u8>,
context: QuoteContext,
) -> Result<(), sp_runtime::DispatchError>;
) -> Result<BoundedVecEncodedVerifyingKey, VerifyQuoteError>;

/// Indicate to the attestation handler that a quote is desired.
///
Expand All @@ -183,12 +184,38 @@ impl<AccountId> AttestationHandler<AccountId> for () {
fn verify_quote(
_attestee: &AccountId,
_x25519_public_key: X25519PublicKey,
_provisioning_certification_key: BoundedVecEncodedVerifyingKey,
_quote: Vec<u8>,
_context: QuoteContext,
) -> Result<(), sp_runtime::DispatchError> {
Ok(())
) -> Result<BoundedVecEncodedVerifyingKey, VerifyQuoteError> {
// 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,
}
6 changes: 0 additions & 6 deletions crates/test-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
/// The mnemonic for the validator stash account to use for the call, should be stash address
#[arg(short, long)]
mnemonic_option: Option<String>,
Expand Down Expand Up @@ -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)?;
Expand All @@ -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));
Expand Down
36 changes: 19 additions & 17 deletions crates/testing-utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
54 changes: 29 additions & 25 deletions crates/threshold-signature-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion crates/threshold-signature-server/src/attestation/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
Loading

0 comments on commit 664e406

Please sign in to comment.