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

Commit dc3828c

Browse files
committed
WIP: Add basic validate and use functionality
1 parent 4929cf4 commit dc3828c

File tree

5 files changed

+131
-13
lines changed

5 files changed

+131
-13
lines changed

rtc_auth_enclave/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.

rtc_auth_enclave/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ jsonwebtoken = { git = "https://github.com/mesalock-linux/jsonwebtoken-sgx" }
2323
# TODO: confirm that we have to use a forked crate here
2424
uuid = { git = "https://github.com/mesalock-linux/uuid-sgx", features = ["v4", "serde"] }
2525
base64 = { git = "https://github.com/mesalock-linux/rust-base64-sgx" }
26+
thiserror = { git = "https://github.com/mesalock-linux/thiserror-sgx.git", tag = "sgx_1.1.3" }
2627

2728
# See "Cargo patch limitation workaround" in HACKING.md:
2829
once_cell = { git = "https://github.com/mesalock-linux/once_cell-sgx.git" }

rtc_auth_enclave/src/jwt.rs

Lines changed: 63 additions & 11 deletions
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

4-
use jsonwebtoken::{encode, EncodingKey, Header};
5+
use jsonwebtoken::{encode, Algorithm, DecodingKey, EncodingKey, Header, TokenData, Validation};
56
use serde::{Deserialize, Serialize};
67
use sgx_tstd::untrusted::time::SystemTimeEx;
8+
use thiserror::Error;
79
use uuid::Uuid;
810

911
use crate::uuid_to_string;
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,16 +28,16 @@ use crate::uuid_to_string;
2228
/// }
2329
/// ```
2430
#[derive(Debug, Serialize, Deserialize)]
25-
struct Claims {
31+
pub(crate) struct Claims {
2632
// Registered Claim Names: https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
2733
// TODO: serialize to hex string? This can be mrenclave or mrsigner
2834
iss: String,
2935
nbf: u64,
3036
iat: u64,
31-
jti: String,
37+
pub(crate) jti: String,
3238
// TODO: Better names. use `x-ntls-mod-hash` etc?
33-
exec_module_hash: String,
34-
dataset_uuid: String,
39+
pub(crate) exec_module_hash: String,
40+
pub(crate) dataset_uuid: String, // TODO: use sub?
3541
dataset_size: u64,
3642
}
3743

@@ -49,7 +55,7 @@ impl Claims {
4955
.as_secs();
5056

5157
Self {
52-
iss: "registree_auth_enclave".to_string(),
58+
iss: CLAIMS_ISS.to_string(),
5359
nbf: now,
5460
iat: now,
5561
jti: token_id,
@@ -60,6 +66,43 @@ impl Claims {
6066
}
6167
}
6268

69+
#[derive(Debug, Error)]
70+
pub(crate) enum DecodeError {
71+
#[error("Decoding failed: {}", .0)]
72+
JWT(#[from] jsonwebtoken::errors::Error),
73+
#[error("Invalid typ field in the jwt header")]
74+
Typ,
75+
}
76+
77+
pub(crate) struct DecodedExecutionToken(TokenData<Claims>);
78+
79+
impl DecodedExecutionToken {
80+
pub(crate) fn decode(token: &str) -> Result<DecodedExecutionToken, DecodeError> {
81+
let validation = Validation {
82+
validate_nbf: true,
83+
iss: Some(CLAIMS_ISS.to_string()),
84+
algorithms: vec![Algorithm::HS256],
85+
86+
..Validation::default()
87+
};
88+
89+
let decoded = jsonwebtoken::decode::<Claims>(token, &get_decoding_key(), &validation)?;
90+
91+
match decoded.header.typ.as_deref() {
92+
Some(HEADER_TYP) => Ok(DecodedExecutionToken(decoded)),
93+
Some(_) | None => Err(DecodeError::Typ),
94+
}
95+
}
96+
97+
pub(crate) fn claims<'a>(&'a self) -> &'a Claims {
98+
&self.0.claims
99+
}
100+
101+
pub(crate) fn header<'a>(&'a self) -> &'a Header {
102+
&self.0.header
103+
}
104+
}
105+
63106
pub(crate) struct EncodedExecutionToken {
64107
pub token: String,
65108
pub token_id: Uuid,
@@ -76,14 +119,12 @@ impl EncodedExecutionToken {
76119
dataset_size,
77120
);
78121

79-
// TODO: Use a signing key that corresponds to the public key
80-
// in the attestation enclave held data and move to crypto module in tenclave.
81-
let encoding_key = EncodingKey::from_secret("secret".as_ref());
122+
let encoding_key = get_encoding_key();
82123
// Header size 48 characters base64
83124
let header = Header {
84125
// Explicit typing for the token type
85126
// SEE: https://datatracker.ietf.org/doc/html/draft-ietf-secevent-token-02#section-2.2
86-
typ: Some("ntlexec+jwt".to_string()),
127+
typ: Some(HEADER_TYP.to_string()),
87128
..Header::default()
88129
};
89130

@@ -94,3 +135,14 @@ impl EncodedExecutionToken {
94135
Self { token, token_id }
95136
}
96137
}
138+
139+
fn get_encoding_key() -> EncodingKey {
140+
// TODO: Use a signing key that corresponds to the public key
141+
// in the attestation enclave held data and move to crypto module in tenclave.
142+
EncodingKey::from_secret("secret".as_ref())
143+
}
144+
145+
fn get_decoding_key<'a>() -> DecodingKey<'a> {
146+
// TODO: Use a decoding key that can is intrinsic to this enclave instance.
147+
DecodingKey::from_secret("secret".as_ref())
148+
}

rtc_auth_enclave/src/lib.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@ mod token_store;
1010
extern crate sgx_tstd as std;
1111

1212
use core::slice;
13-
use std::ptr;
1413
use std::string::{String, ToString};
14+
use std::{ptr, str};
1515

1616
use rtc_tenclave::crypto::{RtcCrypto, SodaBoxCrypto as Crypto};
1717
pub use rtc_tenclave::dh::*;
1818
#[allow(unused_imports)] // for ECALL linking
1919
use rtc_tenclave::enclave::enclave_create_report;
20-
use rtc_types::{EcallResult, EncryptedMessage, ExecReqMetadata, ExecTokenError, IssueTokenResult};
20+
use rtc_types::{
21+
CryptoError,
22+
EcallResult,
23+
EncryptedMessage,
24+
ExecReqMetadata,
25+
ExecTokenError,
26+
IssueTokenResult,
27+
};
2128
use secrecy::{ExposeSecret, Secret};
2229
use serde::{Deserialize, Serialize};
2330
use uuid::Uuid;
@@ -120,6 +127,32 @@ fn validate_dataset_access_key(
120127
Some(20)
121128
}
122129

130+
#[no_mangle]
131+
pub unsafe extern "C" fn validate_and_use_token(
132+
payload_ptr: *const u8,
133+
payload_len: usize,
134+
metadata_ptr: *const ExecReqMetadata,
135+
) {
136+
let payload = unsafe { slice::from_raw_parts(payload_ptr, payload_len) };
137+
let metadata = unsafe { &*metadata_ptr };
138+
validate_and_use_token_impl(payload, metadata);
139+
}
140+
141+
fn validate_and_use_token_impl(
142+
payload: &[u8],
143+
metadata: &ExecReqMetadata,
144+
) -> Result<(), CryptoError> {
145+
let crypto = Crypto::new();
146+
let message = crypto.decrypt_message(payload, &metadata.uploader_pub_key, &metadata.nonce)?;
147+
// TODO: Error handling
148+
let token_str = str::from_utf8(message.expose_secret()).unwrap();
149+
150+
match token_store::validate_and_use(token_str) {
151+
Ok(true) => Ok(()),
152+
Ok(_) | Err(_) => todo!(),
153+
}
154+
}
155+
123156
pub(crate) fn uuid_to_string(uuid: Uuid) -> String {
124157
let mut uuid_buf = Uuid::encode_buffer();
125158
uuid.to_simple().encode_lower(&mut uuid_buf).to_string()

rtc_auth_enclave/src/token_store.rs

Lines changed: 31 additions & 0 deletions
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;
@@ -12,6 +13,7 @@ use sgx_tstd::sync::{SgxMutex as Mutex, SgxMutexGuard as MutexGuard};
1213
use sgx_tstd::untrusted::{fs as untrusted_fs, path as untrusted_path};
1314
use uuid::Uuid;
1415

16+
use crate::jwt::{Claims, DecodedExecutionToken};
1517
use crate::{jwt, uuid_to_string};
1618

1719
#[derive(Serialize, Deserialize)]
@@ -58,6 +60,35 @@ pub(crate) fn issue_token(
5860
Ok(token)
5961
}
6062

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

0 commit comments

Comments
 (0)