Skip to content

Commit 746b1e4

Browse files
authored
Merge pull request #2 from flashbots/peg/support-for-client-attestation
Add support for client attestation
2 parents 16bcfdb + 30a3c94 commit 746b1e4

File tree

4 files changed

+593
-193
lines changed

4 files changed

+593
-193
lines changed

src/attestation.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use sha2::{Digest, Sha256};
2+
use thiserror::Error;
3+
use tokio_rustls::rustls::pki_types::CertificateDer;
4+
use x509_parser::prelude::*;
5+
6+
/// Represents a CVM technology with quote generation and verification
7+
pub trait AttestationPlatform: Clone + Send + 'static {
8+
fn is_cvm(&self) -> bool;
9+
10+
fn create_attestation(
11+
&self,
12+
cert_chain: &[CertificateDer<'_>],
13+
exporter: [u8; 32],
14+
) -> Result<Vec<u8>, AttestationError>;
15+
16+
fn verify_attestation(
17+
&self,
18+
input: Vec<u8>,
19+
cert_chain: &[CertificateDer<'_>],
20+
exporter: [u8; 32],
21+
) -> Result<(), AttestationError>;
22+
}
23+
24+
/// For testing
25+
#[derive(Clone)]
26+
pub struct MockAttestation;
27+
28+
impl AttestationPlatform for MockAttestation {
29+
fn is_cvm(&self) -> bool {
30+
true
31+
}
32+
33+
/// Mocks creating an attestation
34+
fn create_attestation(
35+
&self,
36+
cert_chain: &[CertificateDer<'_>],
37+
exporter: [u8; 32],
38+
) -> Result<Vec<u8>, AttestationError> {
39+
let mut quote_input = [0u8; 64];
40+
let pki_hash = get_pki_hash_from_certificate_chain(cert_chain)?;
41+
quote_input[..32].copy_from_slice(&pki_hash);
42+
quote_input[32..].copy_from_slice(&exporter);
43+
Ok(quote_input.to_vec())
44+
}
45+
46+
/// Mocks verifying an attestation
47+
fn verify_attestation(
48+
&self,
49+
input: Vec<u8>,
50+
cert_chain: &[CertificateDer<'_>],
51+
exporter: [u8; 32],
52+
) -> Result<(), AttestationError> {
53+
let mut quote_input = [0u8; 64];
54+
let pki_hash = get_pki_hash_from_certificate_chain(cert_chain)?;
55+
quote_input[..32].copy_from_slice(&pki_hash);
56+
quote_input[32..].copy_from_slice(&exporter);
57+
58+
if input != quote_input {
59+
return Err(AttestationError::InputMismatch);
60+
}
61+
Ok(())
62+
}
63+
}
64+
65+
/// For no CVM platform (eg: for one-sided remote-attested TLS)
66+
#[derive(Clone)]
67+
pub struct NoAttestation;
68+
69+
impl AttestationPlatform for NoAttestation {
70+
fn is_cvm(&self) -> bool {
71+
false
72+
}
73+
74+
/// Mocks creating an attestation
75+
fn create_attestation(
76+
&self,
77+
_cert_chain: &[CertificateDer<'_>],
78+
_exporter: [u8; 32],
79+
) -> Result<Vec<u8>, AttestationError> {
80+
Ok(Vec::new())
81+
}
82+
83+
/// Mocks verifying an attestation
84+
fn verify_attestation(
85+
&self,
86+
input: Vec<u8>,
87+
_cert_chain: &[CertificateDer<'_>],
88+
_exporter: [u8; 32],
89+
) -> Result<(), AttestationError> {
90+
if input.is_empty() {
91+
Ok(())
92+
} else {
93+
Err(AttestationError::AttestationGivenWhenNoneExpected)
94+
}
95+
}
96+
}
97+
98+
/// Given a certificate chain, get the [Sha256] hash of the public key of the leaf certificate
99+
fn get_pki_hash_from_certificate_chain(
100+
cert_chain: &[CertificateDer<'_>],
101+
) -> Result<[u8; 32], AttestationError> {
102+
let leaf_certificate = cert_chain.first().ok_or(AttestationError::NoCertificate)?;
103+
let (_, cert) = parse_x509_certificate(leaf_certificate.as_ref())?;
104+
let public_key = &cert.tbs_certificate.subject_pki;
105+
let key_bytes = public_key.subject_public_key.as_ref();
106+
107+
let mut hasher = Sha256::new();
108+
hasher.update(key_bytes);
109+
Ok(hasher.finalize().into())
110+
}
111+
112+
/// An error when generating or verifying an attestation
113+
#[derive(Error, Debug)]
114+
pub enum AttestationError {
115+
#[error("Certificate chain is empty")]
116+
NoCertificate,
117+
#[error("X509 parse: {0}")]
118+
X509Parse(#[from] x509_parser::asn1_rs::Err<x509_parser::error::X509Error>),
119+
#[error("X509: {0}")]
120+
X509(#[from] x509_parser::error::X509Error),
121+
#[error("Quote input is not as expected")]
122+
InputMismatch,
123+
#[error("Configuration mismatch - expected no remote attestation")]
124+
AttestationGivenWhenNoneExpected,
125+
}

0 commit comments

Comments
 (0)