Skip to content

Commit

Permalink
Update attestation endpoint in preparation for using the propagation …
Browse files Browse the repository at this point in the history
…pallet to call it
  • Loading branch information
ameba23 committed Aug 16, 2024
1 parent 01213d1 commit 19c7a7a
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 69 deletions.
82 changes: 51 additions & 31 deletions crates/threshold-signature-server/src/attestation/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,52 +13,65 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

use crate::{attestation::errors::AttestationErr, AppState};
use crate::{
attestation::errors::AttestationErr,
chain_api::{entropy, get_api, get_rpc, EntropyConfig},
get_signer_and_x25519_secret,
helpers::substrate::{query_chain, submit_transaction},
AppState,
};
use axum::{body::Bytes, extract::State, http::StatusCode};
use subxt::tx::PairSigner;
use x25519_dalek::StaticSecret;

/// HTTP POST endpoint to initiate a TDX attestation.
/// Not yet implemented.
#[cfg(not(any(test, feature = "unsafe")))]
pub async fn attest(
State(_app_state): State<AppState>,
_input: Bytes,
) -> Result<StatusCode, AttestationErr> {
// Non-mock attestation (the real thing) will go here
Err(AttestationErr::NotImplemented)
}

/// HTTP POST endpoint to initiate a mock TDX attestation for testing on non-TDX hardware.
/// The body of the request should be a 32 byte random nonce used to show 'freshness' of the
/// quote.
/// The response body contains a mock TDX v4 quote serialized as described in the
/// [Index TDX DCAP Quoting Library API](https://download.01.org/intel-sgx/latest/dcap-latest/linux/docs/Intel_TDX_DCAP_Quoting_Library_API.pdf).
#[cfg(any(test, feature = "unsafe"))]
pub async fn attest(
State(app_state): State<AppState>,
input: Bytes,
) -> Result<(StatusCode, Bytes), AttestationErr> {
use crate::{
chain_api::{entropy, get_api, get_rpc},
get_signer_and_x25519_secret,
helpers::substrate::submit_transaction,
};
use rand_core::OsRng;
use sp_core::Pair;

// TODO (#982) confirm with the chain that an attestation should be happenning
let nonce = input.as_ref().try_into()?;
_input: Bytes,
) -> Result<StatusCode, AttestationErr> {
// TODO input should be Vec<AccountId32> - check it

let api = get_api(&app_state.configuration.endpoint).await?;
let rpc = get_rpc(&app_state.configuration.endpoint).await?;
let (signer, x25519_secret) = get_signer_and_x25519_secret(&app_state.kv_store).await?;

let nonce = {
let pending_attestation_query =
entropy::storage().attestation().pending_attestations(signer.account_id());
query_chain(&api, &rpc, pending_attestation_query, None)
.await?
.ok_or_else(|| AttestationErr::Unexpected)?
};

let block_number =
rpc.chain_get_header(None).await?.ok_or_else(|| AttestationErr::BlockNumber)?.number;

let quote = create_quote(block_number, nonce, &signer, &x25519_secret).await?;

let attest_tx = entropy::tx().attestation().attest(quote.clone());
submit_transaction(&api, &rpc, &signer, &attest_tx, None).await?;

Ok(StatusCode::OK)
}

#[cfg(any(test, feature = "unsafe"))]
pub async fn create_quote(
block_number: u32,
nonce: [u8; 32],
signer: &PairSigner<EntropyConfig, sp_core::sr25519::Pair>,
x25519_secret: &StaticSecret,
) -> Result<Vec<u8>, AttestationErr> {
use rand_core::OsRng;
use sp_core::Pair;

// In the real thing this is the hardware key used in the quoting enclave
let signing_key = tdx_quote::SigningKey::random(&mut OsRng);

let (signer, x25519_secret) = get_signer_and_x25519_secret(&app_state.kv_store).await?;
let public_key = x25519_dalek::PublicKey::from(&x25519_secret);
let public_key = x25519_dalek::PublicKey::from(x25519_secret);

let input_data = entropy_shared::QuoteInputData::new(
signer.signer().public(),
Expand All @@ -68,9 +81,16 @@ pub async fn attest(
);

let quote = tdx_quote::Quote::mock(signing_key.clone(), input_data.0).as_bytes().to_vec();
Ok(quote)
}

let attest_tx = entropy::tx().attestation().attest(quote.clone());
submit_transaction(&api, &rpc, &signer, &attest_tx, None).await?;

Ok((StatusCode::OK, Bytes::from(quote)))
#[cfg(not(any(test, feature = "unsafe")))]
pub async fn create_quote(
_block_number: u32,
_nonce: [u8; 32],
_signer: &PairSigner<EntropyConfig, sp_core::sr25519::Pair>,
_x25519_secret: &StaticSecret,
) -> Result<Vec<u8>, AttestationErr> {
// Non-mock attestation (the real thing) will go here
Err(AttestationErr::NotImplemented)
}
3 changes: 2 additions & 1 deletion crates/threshold-signature-server/src/attestation/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ pub enum AttestationErr {
NotImplemented,
#[error("Input must be 32 bytes: {0}")]
TryFromSlice(#[from] TryFromSliceError),
#[cfg(any(test, feature = "unsafe"))]
#[error("Could not get block number")]
BlockNumber,
#[error("Substrate: {0}")]
SubstrateClient(#[from] entropy_client::substrate::SubstrateError),
#[error("UnexpectedAttestationRequest")]
Unexpected,
}

impl IntoResponse for AttestationErr {
Expand Down
80 changes: 43 additions & 37 deletions crates/threshold-signature-server/src/attestation/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,40 +12,46 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
use crate::helpers::tests::{initialize_test_logger, spawn_testing_validators};
use entropy_kvdb::clean_tests;
use entropy_shared::QuoteInputData;
use entropy_testing_utils::{
constants::{TSS_ACCOUNTS, X25519_PUBLIC_KEYS},
substrate_context::test_node_process_testing_state,
};
use serial_test::serial;

#[tokio::test]
#[serial]
async fn test_attest() {
initialize_test_logger().await;
clean_tests();

let _cxt = test_node_process_testing_state(false).await;
let (_validator_ips, _validator_ids) = spawn_testing_validators(false).await;

let nonce = [0; 32];
let client = reqwest::Client::new();
let res = client
.post(format!("http://127.0.0.1:3001/attest"))
.body(nonce.to_vec())
.send()
.await
.unwrap();
assert_eq!(res.status(), 200);
let quote = res.bytes().await.unwrap();

// This internally verifies the signature in the quote
let quote = tdx_quote::Quote::from_bytes(&quote).unwrap();

// Check the input data of the quote
let expected_input_data =
QuoteInputData::new(TSS_ACCOUNTS[0].0, X25519_PUBLIC_KEYS[0], nonce, 0);
assert_eq!(quote.report_input_data(), expected_input_data.0);
}
// use crate::helpers::tests::{initialize_test_logger, spawn_testing_validators};
// use entropy_kvdb::clean_tests;
// use entropy_shared::QuoteInputData;
// use entropy_testing_utils::{
// constants::{TSS_ACCOUNTS, X25519_PUBLIC_KEYS},
// substrate_context::test_node_process_testing_state,
// };
// use serial_test::serial;
//
// #[tokio::test]
// #[serial]
// async fn test_attest() {
// initialize_test_logger().await;
// clean_tests();
//
// let quote = create_quote(
// block_number: u32,
// nonce: [u8; 32],
// signer: &PairSigner<EntropyConfig, sp_core::sr25519::Pair>,
// x25519_secret: &StaticSecret,
// ).unwrap();
// let _cxt = test_node_process_testing_state(false).await;
// let (_validator_ips, _validator_ids) = spawn_testing_validators(false).await;
//
// let nonce = [0; 32];
// let client = reqwest::Client::new();
// let res = client
// .post(format!("http://127.0.0.1:3001/attest"))
// .body(nonce.to_vec())
// .send()
// .await
// .unwrap();
// assert_eq!(res.status(), 200);
// let quote = res.bytes().await.unwrap();
//
// // This internally verifies the signature in the quote
// let quote = tdx_quote::Quote::from_bytes(&quote).unwrap();
//
// // Check the input data of the quote
// let expected_input_data =
// QuoteInputData::new(TSS_ACCOUNTS[0].0, X25519_PUBLIC_KEYS[0], nonce, 0);
// assert_eq!(quote.report_input_data(), expected_input_data.0);
// }

0 comments on commit 19c7a7a

Please sign in to comment.