Skip to content
This repository was archived by the owner on May 9, 2022. It is now read-only.

Commit d19e15e

Browse files
committed
WIP: Add basic validate and use functionality
1 parent cf9f74d commit d19e15e

File tree

5 files changed

+127
-12
lines changed

5 files changed

+127
-12
lines changed

rtc_auth_enclave/Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rtc_auth_enclave/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ serde = { git = "https://github.com/mesalock-linux/serde-sgx", feature = ["deriv
2626
serde_json = { git = "https://github.com/mesalock-linux/serde-json-sgx", tag = "sgx_1.1.3" }
2727
once_cell = { git = "https://github.com/mesalock-linux/once_cell-sgx.git", tag = "sgx_1.1.3" }
2828
base64 = { git = "https://github.com/mesalock-linux/rust-base64-sgx" }
29+
thiserror = { git = "https://github.com/mesalock-linux/thiserror-sgx.git", tag = "sgx_1.1.3" }
2930

3031
secrecy = { version = "0.7.0", default-features = false }
3132

rtc_auth_enclave/src/jwt.rs

+63-11
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
use std::string::{String, ToString};
22
use std::time::{SystemTime, UNIX_EPOCH};
3+
use std::vec;
34

45
use sgx_tstd::untrusted::time::SystemTimeEx;
56

67
use crate::uuid_to_string;
7-
use jsonwebtoken::{encode, EncodingKey, Header};
8+
use jsonwebtoken::{encode, Algorithm, DecodingKey, EncodingKey, Header, TokenData, Validation};
89
use serde::{Deserialize, Serialize};
10+
use thiserror::Error;
911
use uuid::Uuid;
1012

13+
const HEADER_TYP: &'static str = "ntlexec+jwt";
14+
const CLAIMS_ISS: &'static str = "ntls_auth_enclave";
15+
const CLAIMS_AUD: &'static str = "ntls_exec_enclave";
16+
1117
/// Claims body of the JWT token
1218
///
1319
/// Example output:
1420
/// ```
1521
/// {
16-
/// "iss": "registree_auth_enclave",
22+
/// "iss": "ntls_auth_enclave",
1723
/// "nbf": 1623762799,
1824
/// "iat": 1623762799,
1925
/// "jti": "b300fe149d144e05aa9a9600816b42ca",
@@ -22,15 +28,15 @@ use uuid::Uuid;
2228
/// }
2329
/// ```
2430
#[derive(Debug, Serialize, Deserialize)]
25-
struct Claims {
31+
pub(crate) struct Claims {
2632
// TODO: serialize to hex string? This can be mrenclave or mrsigner
2733
iss: String,
2834
nbf: u64,
2935
iat: u64,
30-
jti: String,
36+
pub(crate) jti: String,
3137
// TODO: Better names. use `x-ntls-mod-hash` etc?
32-
exec_module_hash: String,
33-
dataset_uuid: String,
38+
pub(crate) exec_module_hash: String,
39+
pub(crate) dataset_uuid: String, // TODO: use sub?
3440
}
3541

3642
impl Claims {
@@ -42,7 +48,7 @@ impl Claims {
4248
.as_secs();
4349

4450
Self {
45-
iss: "registree_auth_enclave".to_string(),
51+
iss: CLAIMS_ISS.to_string(),
4652
nbf: now,
4753
iat: now,
4854
jti: token_id,
@@ -52,6 +58,43 @@ impl Claims {
5258
}
5359
}
5460

61+
#[derive(Debug, Error)]
62+
pub(crate) enum DecodeError {
63+
#[error("Decoding failed: {}", .0)]
64+
JWT(#[from] jsonwebtoken::errors::Error),
65+
#[error("Invalid typ field in the jwt header")]
66+
Typ,
67+
}
68+
69+
pub(crate) struct DecodedExecutionToken(TokenData<Claims>);
70+
71+
impl DecodedExecutionToken {
72+
pub(crate) fn decode(token: &str) -> Result<DecodedExecutionToken, DecodeError> {
73+
let validation = Validation {
74+
validate_nbf: true,
75+
iss: Some(CLAIMS_ISS.to_string()),
76+
algorithms: vec![Algorithm::HS256],
77+
78+
..Validation::default()
79+
};
80+
81+
let decoded = jsonwebtoken::decode::<Claims>(token, &get_decoding_key(), &validation)?;
82+
83+
match decoded.header.typ.as_deref() {
84+
Some(HEADER_TYP) => Ok(DecodedExecutionToken(decoded)),
85+
Some(_) | None => Err(DecodeError::Typ),
86+
}
87+
}
88+
89+
pub(crate) fn claims<'a>(&'a self) -> &'a Claims {
90+
&self.0.claims
91+
}
92+
93+
pub(crate) fn header<'a>(&'a self) -> &'a Header {
94+
&self.0.header
95+
}
96+
}
97+
5598
pub(crate) struct EncodedExecutionToken {
5699
pub token: String,
57100
pub token_id: Uuid,
@@ -67,14 +110,12 @@ impl EncodedExecutionToken {
67110
uuid_to_string(token_id),
68111
);
69112

70-
// TODO: Use a signing key that corresponds to the public key
71-
// in the attestation enclave held data and move to crypto module in tenclave.
72-
let encoding_key = EncodingKey::from_secret("secret".as_ref());
113+
let encoding_key = get_encoding_key();
73114
// Header size 48 characters base64
74115
let header = Header {
75116
// Explicit typing for the token type
76117
// SEE: https://datatracker.ietf.org/doc/html/draft-ietf-secevent-token-02#section-2.2
77-
typ: Some("ntlexec+jwt".to_string()),
118+
typ: Some(HEADER_TYP.to_string()),
78119
..Header::default()
79120
};
80121

@@ -85,3 +126,14 @@ impl EncodedExecutionToken {
85126
Self { token, token_id }
86127
}
87128
}
129+
130+
fn get_encoding_key() -> EncodingKey {
131+
// TODO: Use a signing key that corresponds to the public key
132+
// in the attestation enclave held data and move to crypto module in tenclave.
133+
EncodingKey::from_secret("secret".as_ref())
134+
}
135+
136+
fn get_decoding_key<'a>() -> DecodingKey<'a> {
137+
// TODO: Use a decoding key that can is intrinsic to this enclave instance.
138+
DecodingKey::from_secret("secret".as_ref())
139+
}

rtc_auth_enclave/src/lib.rs

+30-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ extern crate sgx_tstd as std;
1111

1212
use core::slice;
1313
use std::ptr;
14+
use std::str;
1415
use std::string::{String, ToString};
1516

1617
use rtc_tenclave::crypto::RtcCrypto;
@@ -19,7 +20,9 @@ pub use rtc_tenclave::dh::*;
1920

2021
#[allow(unused_imports)] // for ECALL linking
2122
use rtc_tenclave::enclave::enclave_create_report;
22-
use rtc_types::{EcallResult, EncryptedMessage, ExecReqMetadata, ExecTokenError, IssueTokenResult};
23+
use rtc_types::{
24+
CryptoError, EcallResult, EncryptedMessage, ExecReqMetadata, ExecTokenError, IssueTokenResult,
25+
};
2326
use secrecy::{ExposeSecret, Secret};
2427
use serde::{Deserialize, Serialize};
2528
use uuid::Uuid;
@@ -114,6 +117,32 @@ fn valid_dataset_access_key(_dataset_uuid: [u8; 16], _access_key: [u8; 24]) -> b
114117
true
115118
}
116119

120+
#[no_mangle]
121+
pub unsafe extern "C" fn validate_and_use_token(
122+
payload_ptr: *const u8,
123+
payload_len: usize,
124+
metadata_ptr: *const ExecReqMetadata,
125+
) {
126+
let payload = unsafe { slice::from_raw_parts(payload_ptr, payload_len) };
127+
let metadata = unsafe { &*metadata_ptr };
128+
validate_and_use_token_impl(payload, metadata);
129+
}
130+
131+
fn validate_and_use_token_impl(
132+
payload: &[u8],
133+
metadata: &ExecReqMetadata,
134+
) -> Result<(), CryptoError> {
135+
let crypto = Crypto::new();
136+
let message = crypto.decrypt_message(payload, &metadata.uploader_pub_key, &metadata.nonce)?;
137+
// TODO: Error handling
138+
let token_str = str::from_utf8(message.expose_secret()).unwrap();
139+
140+
match token_store::validate_and_use(token_str) {
141+
Ok(true) => Ok(()),
142+
Ok(_) | Err(_) => todo!(),
143+
}
144+
}
145+
117146
pub(crate) fn uuid_to_string(uuid: Uuid) -> String {
118147
let mut uuid_buf = Uuid::encode_buffer();
119148
uuid.to_simple().encode_lower(&mut uuid_buf).to_string()

rtc_auth_enclave/src/token_store.rs

+32
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use core::str::FromStr;
12
use std::collections::HashMap;
23
use std::io;
34
use std::path::Path;
@@ -17,6 +18,8 @@ use serde::{Deserialize, Serialize};
1718
use uuid::Uuid;
1819

1920
use crate::jwt;
21+
use crate::jwt::Claims;
22+
use crate::jwt::DecodedExecutionToken;
2023
use crate::uuid_to_string;
2124

2225
#[derive(Serialize, Deserialize)]
@@ -62,6 +65,35 @@ pub(crate) fn issue_token(
6265
Ok(token)
6366
}
6467

68+
pub(crate) fn validate_and_use(token: &str) -> Result<bool, io::Error> {
69+
let mut store = kv_store();
70+
// TODO: Error handling
71+
let decoded_token = DecodedExecutionToken::decode(token).unwrap();
72+
let claims = decoded_token.claims();
73+
74+
match store.load(&claims.dataset_uuid)? {
75+
// TODO: error handling
76+
Some(mut records) => match records.get_mut(&Uuid::from_str(&claims.jti).unwrap()) {
77+
Some(mut record) if record_is_valid(claims, record).unwrap() => {
78+
record.current_uses += 1;
79+
store.save(&claims.dataset_uuid, &records)?;
80+
Ok(true)
81+
}
82+
Some(_) | None => Ok(false),
83+
},
84+
None => Ok(false),
85+
}
86+
}
87+
88+
fn record_is_valid(claims: &Claims, record: &ExecutionTokenRecord) -> Result<bool, uuid::Error> {
89+
let has_uses = record.allowed_uses > record.current_uses;
90+
let for_correct_dataset = record.dataset_uuid == Uuid::from_str(&claims.dataset_uuid)?;
91+
let for_correct_exec_module =
92+
base64::encode(record.exec_module_hash) == claims.exec_module_hash;
93+
94+
Ok(has_uses && for_correct_dataset && for_correct_exec_module)
95+
}
96+
6597
fn save_token(
6698
dataset_uuid: Uuid,
6799
token_uuid: Uuid,

0 commit comments

Comments
 (0)