Skip to content

Commit 03b9f86

Browse files
authored
Merge pull request #32 from flashbots/peg/attestation-encoding
Use SCALE rather than JSON for encoding attestation payloads
2 parents d55a894 + 69eb974 commit 03b9f86

File tree

5 files changed

+74
-35
lines changed

5 files changed

+74
-35
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ bytes = "1.10.1"
2727
http = "1.3.1"
2828
serde_json = "1.0.145"
2929
serde = "1.0.228"
30+
parity-scale-codec = "3.7.5"
3031

3132
[dev-dependencies]
3233
rcgen = "0.14.5"

src/attestation/mod.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub mod measurements;
22

33
use measurements::{CvmImageMeasurements, MeasurementRecord, Measurements, PlatformMeasurements};
4+
use parity_scale_codec::{Decode, Encode};
45
use serde::{Deserialize, Serialize};
56
use std::{
67
fmt::{self, Display, Formatter},
@@ -22,13 +23,19 @@ use x509_parser::prelude::*;
2223
/// For fetching collateral directly from intel, if no PCCS is specified
2324
const PCS_URL: &str = "https://api.trustedservices.intel.com";
2425

25-
#[derive(Debug, Serialize, Deserialize)]
26+
/// This is the type sent over the channel to provide an attestation
27+
#[derive(Debug, Serialize, Deserialize, Encode, Decode)]
2628
pub struct AttesationPayload {
29+
/// What CVM platform is used (including none)
2730
pub attestation_type: AttestationType,
31+
/// The attestation evidence as bytes - in the case of DCAP this is a quote
2832
pub attestation: Vec<u8>,
2933
}
3034

3135
impl AttesationPayload {
36+
/// Given an attestation generator (quote generation function for a specific platform)
37+
/// return an attestation
38+
/// This also takes the certificate chain and exporter as they are given as input to the attestation
3239
pub fn from_attestation_generator(
3340
cert_chain: &[CertificateDer<'_>],
3441
exporter: [u8; 32],
@@ -39,6 +46,15 @@ impl AttesationPayload {
3946
attestation: attesation_generator.create_attestation(cert_chain, exporter)?,
4047
})
4148
}
49+
50+
/// Create an empty attestation payload for the case that we are running in a non-confidential
51+
/// environment
52+
pub fn without_attestation() -> Self {
53+
Self {
54+
attestation_type: AttestationType::None,
55+
attestation: Vec::new(),
56+
}
57+
}
4258
}
4359

4460
/// Type of attestaion used
@@ -73,6 +89,7 @@ impl AttestationType {
7389
}
7490
}
7591

92+
/// Get a quote generator for this type of platform
7693
pub fn get_quote_generator(&self) -> Result<Arc<dyn QuoteGenerator>, AttestationError> {
7794
match self {
7895
AttestationType::None => Ok(Arc::new(NoQuoteGenerator)),
@@ -85,6 +102,23 @@ impl AttestationType {
85102
}
86103
}
87104

105+
/// SCALE encode (used over the wire)
106+
impl Encode for AttestationType {
107+
fn encode(&self) -> Vec<u8> {
108+
self.as_str().encode()
109+
}
110+
}
111+
112+
/// SCALE decode
113+
impl Decode for AttestationType {
114+
fn decode<I: parity_scale_codec::Input>(
115+
input: &mut I,
116+
) -> Result<Self, parity_scale_codec::Error> {
117+
let s: String = String::decode(input)?;
118+
serde_json::from_str(&format!("\"{s}\"")).map_err(|_| "Failed to decode enum".into())
119+
}
120+
}
121+
88122
impl Display for AttestationType {
89123
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
90124
f.write_str(self.as_str())
@@ -104,9 +138,15 @@ pub trait QuoteGenerator: Send + Sync + 'static {
104138
) -> Result<Vec<u8>, AttestationError>;
105139
}
106140

141+
/// Allows remote attestations to be verified
107142
#[derive(Clone, Debug)]
108143
pub struct AttestationVerifier {
144+
/// The measurement values we accept
145+
///
146+
/// If this is empty, anything will be accepted - but measurements are always injected into HTTP
147+
/// headers, so that they can be verified upstream
109148
accepted_measurements: Vec<MeasurementRecord>,
149+
/// A PCCS service to use - defaults to Intel PCS
110150
pccs_url: Option<String>,
111151
}
112152

src/lib.rs

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ use attestation::{measurements::Measurements, AttestationError, AttestationType}
44
pub use attestation::{DcapTdxQuoteGenerator, NoQuoteGenerator, QuoteGenerator};
55
use bytes::Bytes;
66
use http::HeaderValue;
7-
use http_body_util::combinators::BoxBody;
8-
use http_body_util::BodyExt;
9-
use hyper::service::service_fn;
10-
use hyper::Response;
7+
use http_body_util::{combinators::BoxBody, BodyExt};
8+
use hyper::{service::service_fn, Response};
119
use hyper_util::rt::TokioIo;
10+
use parity_scale_codec::{Decode, Encode};
1211
use thiserror::Error;
1312
use tokio::sync::{mpsc, oneshot};
1413
use tokio_rustls::rustls::server::{VerifierBuilderError, WebPkiClientVerifier};
@@ -191,15 +190,12 @@ impl ProxyServer {
191190
let remote_cert_chain = connection.peer_certificates().map(|c| c.to_owned());
192191

193192
// If we are in a CVM, generate an attestation
194-
let attestation = if local_quote_generator.attestation_type() != AttestationType::None {
195-
serde_json::to_vec(&AttesationPayload::from_attestation_generator(
196-
&cert_chain,
197-
exporter,
198-
local_quote_generator,
199-
)?)?
200-
} else {
201-
Vec::new()
202-
};
193+
let attestation = AttesationPayload::from_attestation_generator(
194+
&cert_chain,
195+
exporter,
196+
local_quote_generator,
197+
)?
198+
.encode();
203199

204200
// Write our attestation to the channel, with length prefix
205201
let attestation_length_prefix = length_prefix(&attestation);
@@ -215,24 +211,20 @@ impl ProxyServer {
215211
let mut buf = vec![0; length];
216212
tls_stream.read_exact(&mut buf).await?;
217213

214+
let remote_attestation_payload = AttesationPayload::decode(&mut &buf[..])?;
215+
let remote_attestation_type = remote_attestation_payload.attestation_type;
216+
218217
// If we expect an attestaion from the client, verify it and get measurements
219-
let (measurements, remote_attestation_type) = if attestation_verifier.has_remote_attestion()
220-
{
221-
let remote_attestation_payload: AttesationPayload = serde_json::from_slice(&buf)?;
222-
223-
let remote_attestation_type = remote_attestation_payload.attestation_type;
224-
(
225-
attestation_verifier
226-
.verify_attestation(
227-
remote_attestation_payload,
228-
&remote_cert_chain.ok_or(ProxyError::NoClientAuth)?,
229-
exporter,
230-
)
231-
.await?,
232-
remote_attestation_type,
233-
)
218+
let measurements = if attestation_verifier.has_remote_attestion() {
219+
attestation_verifier
220+
.verify_attestation(
221+
remote_attestation_payload,
222+
&remote_cert_chain.ok_or(ProxyError::NoClientAuth)?,
223+
exporter,
224+
)
225+
.await?
234226
} else {
235-
(None, AttestationType::None)
227+
None
236228
};
237229

238230
// Setup an HTTP server
@@ -607,7 +599,7 @@ impl ProxyClient {
607599
let mut buf = vec![0; length];
608600
tls_stream.read_exact(&mut buf).await?;
609601

610-
let remote_attestation_payload: AttesationPayload = serde_json::from_slice(&buf)?;
602+
let remote_attestation_payload = AttesationPayload::decode(&mut &buf[..])?;
611603
let remote_attestation_type = remote_attestation_payload.attestation_type;
612604

613605
// Verify the remote attestation against our accepted measurements
@@ -617,13 +609,14 @@ impl ProxyClient {
617609

618610
// If we are in a CVM, provide an attestation
619611
let attestation = if local_quote_generator.attestation_type() != AttestationType::None {
620-
serde_json::to_vec(&AttesationPayload::from_attestation_generator(
612+
AttesationPayload::from_attestation_generator(
621613
&cert_chain.ok_or(ProxyError::NoClientAuth)?,
622614
exporter,
623615
local_quote_generator,
624-
)?)?
616+
)?
617+
.encode()
625618
} else {
626-
Vec::new()
619+
AttesationPayload::without_attestation().encode()
627620
};
628621

629622
// Send our attestation (or zero bytes) prefixed with length
@@ -705,7 +698,7 @@ async fn get_tls_cert_with_config(
705698
let mut buf = vec![0; length];
706699
tls_stream.read_exact(&mut buf).await?;
707700

708-
let remote_attestation_payload: AttesationPayload = serde_json::from_slice(&buf)?;
701+
let remote_attestation_payload = AttesationPayload::decode(&mut &buf[..])?;
709702

710703
let _measurements = attestation_verifier
711704
.verify_attestation(remote_attestation_payload, &remote_cert_chain, exporter)
@@ -741,6 +734,8 @@ pub enum ProxyError {
741734
OneShotRecv(#[from] oneshot::error::RecvError),
742735
#[error("Failed to send request, connection to proxy-server dropped")]
743736
MpscSend,
737+
#[error("Serialization: {0}")]
738+
Serialization(#[from] parity_scale_codec::Error),
744739
}
745740

746741
impl From<mpsc::error::SendError<RequestWithResponseSender>> for ProxyError {

src/main.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,13 @@ fn load_tls_cert_and_key(
243243
Ok(TlsCertAndKey { key, cert_chain })
244244
}
245245

246+
/// load certificates from a PEM-encoded file
246247
fn load_certs_pem(path: PathBuf) -> std::io::Result<Vec<CertificateDer<'static>>> {
247248
rustls_pemfile::certs(&mut std::io::BufReader::new(File::open(path)?))
248249
.collect::<Result<Vec<_>, _>>()
249250
}
250251

252+
/// load TLS private key from a PEM-encoded file
251253
fn load_private_key_pem(path: PathBuf) -> anyhow::Result<PrivateKeyDer<'static>> {
252254
let mut reader = std::io::BufReader::new(File::open(path)?);
253255

0 commit comments

Comments
 (0)