-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
TSS attestation endpoint #1001
TSS attestation endpoint #1001
Changes from all commits
11f4b39
f795f69
5881b0e
5c73ab4
10ee387
3b9d337
3fb58a1
fe60c04
bebc2f8
f9afd5f
1b7309d
e03014e
2f0e2ae
30f67d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -70,6 +70,7 @@ 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", optional=true, features=["mock"] } | ||
|
||
[dev-dependencies] | ||
serial_test ="3.1.1" | ||
|
@@ -83,6 +84,7 @@ 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 ={ git="https://github.com/entropyxyz/tdx-quote", features=["mock"] } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to have this in both norm deps and dev deps? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the problem is if we don't, it wont compile in test mode without the |
||
|
||
# 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. | ||
|
@@ -102,7 +104,7 @@ vergen={ version="8.3.2", features=["build", "git", "gitcl"] } | |
default =['std'] | ||
std =["sp-core/std"] | ||
test_helpers=["dep:project-root"] | ||
unsafe =[] | ||
unsafe =["dep:tdx-quote"] | ||
alice =[] | ||
bob =[] | ||
# Enable this feature to run the integration tests for the wasm API of entropy-protocol | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// 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 <https://www.gnu.org/licenses/>. | ||
|
||
use crate::{attestation::errors::AttestationErr, AppState}; | ||
use axum::{body::Bytes, extract::State, http::StatusCode}; | ||
|
||
/// 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::get_rpc, get_signer_and_x25519_secret}; | ||
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()?; | ||
|
||
let rpc = get_rpc(&app_state.configuration.endpoint).await?; | ||
|
||
let block_number = | ||
rpc.chain_get_header(None).await?.ok_or_else(|| AttestationErr::BlockNumber)?.number; | ||
|
||
// 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 input_data = entropy_shared::QuoteInputData::new( | ||
signer.signer().public().into(), | ||
*public_key.as_bytes(), | ||
nonce, | ||
block_number, | ||
); | ||
|
||
let quote = tdx_quote::Quote::mock(signing_key.clone(), input_data.0); | ||
// Here we would submit an attest extrinsic to the chain - but for now we just include it in the | ||
// response | ||
Ok((StatusCode::OK, Bytes::from(quote.as_bytes().to_vec()))) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// 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 <https://www.gnu.org/licenses/>. | ||
|
||
use std::array::TryFromSliceError; | ||
|
||
use axum::{ | ||
http::StatusCode, | ||
response::{IntoResponse, Response}, | ||
}; | ||
use thiserror::Error; | ||
|
||
#[derive(Debug, Error)] | ||
pub enum AttestationErr { | ||
#[error("Generic Substrate error: {0}")] | ||
GenericSubstrate(#[from] subxt::error::Error), | ||
#[error("User Error: {0}")] | ||
UserErr(#[from] crate::user::UserErr), | ||
#[cfg(not(any(test, feature = "unsafe")))] | ||
#[error("Not yet implemented")] | ||
NotImplemented, | ||
#[error("Input must be 32 bytes: {0}")] | ||
TryFromSlice(#[from] TryFromSliceError), | ||
#[cfg(any(test, feature = "unsafe"))] | ||
#[error("Could not get block number")] | ||
BlockNumber, | ||
} | ||
|
||
impl IntoResponse for AttestationErr { | ||
fn into_response(self) -> Response { | ||
tracing::error!("{:?}", format!("{self}")); | ||
let body = format!("{self}").into_bytes(); | ||
(StatusCode::INTERNAL_SERVER_ERROR, body).into_response() | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// 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 <https://www.gnu.org/licenses/>. | ||
|
||
//! Makes attestations that this program is running on TDX hardware | ||
pub mod api; | ||
pub mod errors; | ||
|
||
#[cfg(test)] | ||
mod tests; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
// 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 <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("e).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); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,6 +122,7 @@ | |
//! [sled](https://docs.rs/sled) | ||
#![doc(html_logo_url = "https://entropy.xyz/assets/logo_02.png")] | ||
pub use entropy_client::chain_api; | ||
pub(crate) mod attestation; | ||
pub(crate) mod health; | ||
pub mod helpers; | ||
pub(crate) mod node_info; | ||
|
@@ -149,6 +150,7 @@ pub use crate::helpers::{ | |
validator::{get_signer, get_signer_and_x25519_secret}, | ||
}; | ||
use crate::{ | ||
attestation::api::attest, | ||
health::api::healthz, | ||
launch::Configuration, | ||
node_info::api::{hashes, version as get_version}, | ||
|
@@ -178,6 +180,7 @@ pub fn app(app_state: AppState) -> Router { | |
.route("/user/sign_tx", post(sign_tx)) | ||
.route("/signer/proactive_refresh", post(proactive_refresh)) | ||
.route("/validator/reshare", post(new_reshare)) | ||
.route("/attest", post(attest)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this should go lower into the unsafe endpoints There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i can put it there for now but the idea is this same endpoint will be the real thing when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. idk we can always change it when we remove the unsafe, it makes sense to me but your call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FWIW I think it's fine as is |
||
.route("/healthz", get(healthz)) | ||
.route("/version", get(get_version)) | ||
.route("/hashes", get(hashes)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is in
entropy-shared
because it will also be used by the chain when verifying quote input