1use std::{
3 collections::VecDeque,
4 sync::{Arc, RwLock},
5};
6
7use anyhow::Result;
8use base64::prelude::*;
9use rand::{rngs::OsRng, Rng};
10use sgx_isa::Targetinfo;
11use thiserror::Error;
12use tiny_keccak::{Hasher, TupleHash};
13
14use crate::{
15 common::{
16 crypto::{
17 hash::Hash,
18 mrae::deoxysii::{self, Opener},
19 signature::{self, Signature, Signer},
20 x25519,
21 },
22 sgx::{self, EnclaveIdentity, Quote, QuotePolicy, VerifiedQuote},
23 time::insecure_posix_time,
24 },
25 consensus::registry::EndorsedCapabilityTEE,
26 TeeType, BUILD_INFO,
27};
28
29const RAK_HASH_CONTEXT: &[u8] = b"oasis-core/node: TEE RAK binding";
31const QUOTE_NONCE_CONTEXT: &[u8] = b"oasis-core/node: TEE quote nonce";
33
34const INSECURE_RAK_SEED: &str = "ekiden test key manager RAK seed";
36const INSECURE_REK_SEED: &str = "ekiden test key manager REK seed";
38
39#[derive(Error, Debug)]
41enum IdentityError {
42 #[error("RAK binding mismatch")]
43 BindingMismatch,
44 #[error("malformed report data")]
45 MalformedReportData,
46}
47
48#[derive(Error, Debug)]
50enum QuoteError {
51 #[error("target info not set")]
52 TargetInfoNotSet,
53 #[error("malformed target_info")]
54 MalformedTargetInfo,
55 #[error("MRENCLAVE mismatch")]
56 MrEnclaveMismatch,
57 #[error("MRSIGNER mismatch")]
58 MrSignerMismatch,
59 #[error("quote nonce mismatch")]
60 NonceMismatch,
61 #[error("quote policy not set")]
62 QuotePolicyNotSet,
63 #[error("node identity not set")]
64 NodeIdentityNotSet,
65 #[error("endorsed quote mismatch")]
66 EndorsedQuoteMismatch,
67}
68
69struct Inner {
70 rak: signature::PrivateKey,
71 rek: x25519::PrivateKey,
72 quote: Option<Arc<Quote>>,
73 quote_timestamp: Option<i64>,
74 quote_policy: Option<Arc<QuotePolicy>>,
75 known_quotes: VecDeque<Arc<Quote>>,
76 enclave_identity: Option<EnclaveIdentity>,
77 node_identity: Option<signature::PublicKey>,
78 endorsed_capability_tee: Option<EndorsedCapabilityTEE>,
79 target_info: Option<Targetinfo>,
80 nonce: Option<[u8; 32]>,
81}
82
83pub struct Identity {
93 inner: RwLock<Inner>,
94}
95
96impl Default for Identity {
97 fn default() -> Self {
98 Self::new()
99 }
100}
101
102impl Identity {
103 pub fn new() -> Self {
105 let (rak, rek) = match BUILD_INFO.tee_type {
106 TeeType::None => {
107 assert!(!BUILD_INFO.is_secure);
109
110 (
111 signature::PrivateKey::from_test_seed(INSECURE_RAK_SEED.to_string()),
112 x25519::PrivateKey::from_test_seed(INSECURE_REK_SEED.to_string()),
113 )
114 }
115 _ => {
116 (
118 signature::PrivateKey::generate(),
119 x25519::PrivateKey::generate(),
120 )
121 }
122 };
123
124 Self {
125 inner: RwLock::new(Inner {
126 rak,
127 rek,
128 quote: None,
129 quote_timestamp: None,
130 quote_policy: None,
131 known_quotes: Default::default(),
132 enclave_identity: EnclaveIdentity::current(),
133 node_identity: None,
134 endorsed_capability_tee: None,
135 target_info: None,
136 nonce: None,
137 }),
138 }
139 }
140
141 fn report_body_for_rak(rak: &signature::PublicKey) -> Hash {
143 let mut message = [0; 64];
144 message[0..32].copy_from_slice(RAK_HASH_CONTEXT);
145 message[32..64].copy_from_slice(rak.as_ref());
146 Hash::digest_bytes(&message)
147 }
148
149 fn generate_nonce() -> [u8; 32] {
151 let mut nonce_bytes = [0u8; 32];
152 OsRng.fill(&mut nonce_bytes);
153
154 let mut h = TupleHash::v256(QUOTE_NONCE_CONTEXT);
155 h.update(&nonce_bytes);
156 h.finalize(&mut nonce_bytes);
157
158 nonce_bytes
159 }
160
161 fn get_sgx_target_info(&self) -> Option<Targetinfo> {
163 let inner = self.inner.read().unwrap();
164 inner.target_info.clone()
165 }
166
167 pub(crate) fn init_target_info(&self, target_info: Vec<u8>) -> Result<()> {
169 match BUILD_INFO.tee_type {
170 TeeType::Sgx => {
171 let mut inner = self.inner.write().unwrap();
172
173 let target_info = match Targetinfo::try_copy_from(&target_info) {
176 Some(target_info) => target_info,
177 None => return Err(QuoteError::MalformedTargetInfo.into()),
178 };
179 inner.target_info = Some(target_info);
180
181 Ok(())
182 }
183 TeeType::Tdx => {
184 if !target_info.is_empty() {
186 return Err(QuoteError::MalformedTargetInfo.into());
187 }
188
189 Ok(())
190 }
191 TeeType::None => Ok(()),
192 }
193 }
194
195 pub(crate) fn init_report(
197 &self,
198 ) -> Result<(signature::PublicKey, x25519::PublicKey, Vec<u8>, String)> {
199 let rak_pub = self.public_rak();
200 let rek_pub = self.public_rek();
201
202 let nonce = Self::generate_nonce();
204 let report_body = Self::report_body_for_rak(&rak_pub);
206 let mut report_data = [0; 64];
207 report_data[0..32].copy_from_slice(report_body.as_ref());
208 report_data[32..64].copy_from_slice(nonce.as_ref());
209
210 let result = match BUILD_INFO.tee_type {
211 TeeType::Sgx => {
212 let target_info = self
213 .get_sgx_target_info()
214 .ok_or(QuoteError::TargetInfoNotSet)?;
215
216 let quote_nonce = BASE64_STANDARD.encode(&nonce[..24]);
220
221 let report = sgx::report_for(&target_info, &report_data);
222 let report: &[u8] = report.as_ref();
223 let report = report.to_vec();
224
225 (rak_pub, rek_pub, report, quote_nonce)
229 }
230 #[cfg(feature = "tdx")]
231 TeeType::Tdx => {
232 let quote = crate::common::tdx::report::get_quote(&report_data)?;
234
235 (rak_pub, rek_pub, quote, String::new())
236 }
237 _ => panic!("init_report called outside TEE environment"),
238 };
239
240 let mut inner = self.inner.write().unwrap();
242 inner.nonce = Some(nonce);
243
244 Ok(result)
245 }
246
247 pub(crate) fn set_quote(
249 &self,
250 node_id: signature::PublicKey,
251 quote: Quote,
252 ) -> Result<VerifiedQuote> {
253 let rak_pub = self.public_rak();
254
255 let mut inner = self.inner.write().unwrap();
256
257 let expected_nonce = match &inner.nonce {
259 Some(nonce) => *nonce,
260 None => return Err(QuoteError::NonceMismatch.into()),
261 };
262
263 inner.nonce = None;
268
269 let policy = inner
270 .quote_policy
271 .as_ref()
272 .ok_or(QuoteError::QuotePolicyNotSet)?;
273 let verified_quote = quote.verify(policy)?;
274 let nonce = &verified_quote.report_data[32..];
275 if expected_nonce.as_ref() != nonce {
276 return Err(QuoteError::NonceMismatch.into());
277 }
278
279 let enclave_identity = inner
281 .enclave_identity
282 .as_ref()
283 .expect("Enclave identity must be configured");
284 if verified_quote.identity.mr_enclave != enclave_identity.mr_enclave {
285 return Err(QuoteError::MrEnclaveMismatch.into());
286 }
287 if verified_quote.identity.mr_signer != enclave_identity.mr_signer {
288 return Err(QuoteError::MrSignerMismatch.into());
289 }
290
291 Self::verify_binding(&verified_quote, &rak_pub)?;
293
294 if inner.quote.is_some() {
297 let existing_timestamp = inner.quote_timestamp.unwrap();
298 if existing_timestamp > verified_quote.timestamp {
299 return Ok(verified_quote);
300 }
301 }
302
303 match inner.node_identity {
305 Some(existing_node_id) if node_id != existing_node_id => {
306 panic!("host node identity may never change");
307 }
308 Some(_) => {} None => inner.node_identity = Some(node_id),
310 }
311
312 let quote = Arc::new(quote);
313 inner.quote = Some(quote.clone());
314 inner.quote_timestamp = Some(verified_quote.timestamp);
315
316 inner.known_quotes.push_back(quote);
319 if inner.known_quotes.len() > 2 {
320 inner.known_quotes.pop_front();
321 }
322
323 Ok(verified_quote)
324 }
325
326 pub(crate) fn set_quote_policy(&self, policy: QuotePolicy) -> Result<()> {
328 let mut inner = self.inner.write().unwrap();
329 inner.quote_policy = Some(Arc::new(policy));
330
331 Ok(())
332 }
333
334 pub(crate) fn set_endorsed_capability_tee(&self, ect: EndorsedCapabilityTEE) -> Result<()> {
336 if !ect.capability_tee.matches(self) {
338 return Err(QuoteError::EndorsedQuoteMismatch.into());
339 }
340
341 let mut inner = self.inner.write().unwrap();
342 let policy = inner
343 .quote_policy
344 .as_ref()
345 .ok_or(QuoteError::QuotePolicyNotSet)?;
346 let node_id = inner.node_identity.ok_or(QuoteError::NodeIdentityNotSet)?;
347
348 if ect.node_endorsement.public_key != node_id {
350 return Err(QuoteError::EndorsedQuoteMismatch.into());
351 }
352 ect.verify(policy)?;
353
354 inner.endorsed_capability_tee = Some(ect);
355
356 Ok(())
357 }
358
359 pub fn endorsed_capability_tee(&self) -> Option<EndorsedCapabilityTEE> {
361 let inner = self.inner.read().unwrap();
362 inner.endorsed_capability_tee.clone()
363 }
364
365 pub fn node_identity(&self) -> Option<signature::PublicKey> {
367 let inner = self.inner.read().unwrap();
368 inner.node_identity
369 }
370
371 pub fn public_rak(&self) -> signature::PublicKey {
376 let inner = self.inner.read().unwrap();
377 inner.rak.public_key()
378 }
379
380 pub fn public_rek(&self) -> x25519::PublicKey {
385 let inner = self.inner.read().unwrap();
386 inner.rek.public_key()
387 }
388
389 pub fn quote(&self) -> Option<Arc<Quote>> {
394 let now = insecure_posix_time();
395
396 let mut inner = self.inner.write().unwrap();
398 if inner.quote.is_some() {
399 let quote = inner.quote.as_ref().unwrap();
400 let timestamp = inner.quote_timestamp.unwrap();
401 let quote_policy = inner.quote_policy.as_ref().unwrap();
402
403 if !quote.is_fresh(now, timestamp, quote_policy) {
404 inner.quote = None;
406 inner.quote_timestamp = None;
407 inner.quote_policy = None;
408
409 return None;
410 }
411 }
412
413 inner.quote.clone()
414 }
415
416 pub fn quote_policy(&self) -> Option<Arc<QuotePolicy>> {
422 let inner = self.inner.read().unwrap();
423 inner.quote_policy.clone()
424 }
425
426 pub fn verify_binding(quote: &VerifiedQuote, rak: &signature::PublicKey) -> Result<()> {
428 if quote.report_data.len() < 32 {
429 return Err(IdentityError::MalformedReportData.into());
430 }
431 if Self::report_body_for_rak(rak).as_ref() != "e.report_data[..32] {
432 return Err(IdentityError::BindingMismatch.into());
433 }
434
435 Ok(())
436 }
437
438 pub fn rak_matches(&self, rak: &signature::PublicKey, quote: &Quote) -> bool {
440 if &self.public_rak() != rak {
442 return false;
443 }
444
445 let inner = self.inner.read().unwrap();
446 inner.known_quotes.iter().any(|q| &**q == quote)
447 }
448}
449
450impl Signer for Identity {
451 fn public(&self) -> signature::PublicKey {
452 let inner = self.inner.read().unwrap();
453 inner.rak.public_key()
454 }
455
456 fn sign(&self, context: &[u8], message: &[u8]) -> Result<Signature> {
457 let inner = self.inner.read().unwrap();
458 inner.rak.sign(context, message)
459 }
460}
461
462impl Opener for Identity {
463 fn box_open(
464 &self,
465 nonce: &[u8; deoxysii::NONCE_SIZE],
466 ciphertext: Vec<u8>,
467 additional_data: Vec<u8>,
468 peers_public_key: &x25519_dalek::PublicKey,
469 ) -> Result<Vec<u8>> {
470 let inner = self.inner.read().unwrap();
471 let private_key = &inner.rek.0;
472
473 deoxysii::box_open(
474 nonce,
475 ciphertext,
476 additional_data,
477 peers_public_key,
478 private_key,
479 )
480 }
481}