Skip to content

Commit 905b985

Browse files
committed
client: add provision client
1 parent e0e8a5e commit 905b985

File tree

6 files changed

+106
-42
lines changed

6 files changed

+106
-42
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.

common/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ once_cell = "1"
6363
rand_core = { version = "0.6.3", default-features = false, features = ["alloc"] }
6464
# Easily generate x509 certs with ring
6565
rcgen = { version = "0.9", default-features = false, features = [] }
66+
# Used to make HTTP requests to the API
67+
reqwest = { version = "0.11", default-features = false, features = ["json", "rustls-tls-manual-roots"] }
6668
# Safe and small crypto primitives based on BoringSSL
6769
# NOTE: version must exactly match patched version
6870
ring = "=0.16.20"

common/src/api/provision.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,20 @@ use serde::{Deserialize, Serialize};
33

44
use crate::api::UserPk;
55
use crate::enclave::{MachineId, Measurement, MinCpusvn};
6+
use crate::root_seed::RootSeed;
7+
8+
/// The client sends this provisioning request to the node.
9+
#[derive(Serialize, Deserialize)]
10+
pub struct ProvisionRequest {
11+
/// The client's user pk.
12+
pub user_pk: UserPk,
13+
/// The client's node public key, derived from the root seed. The node
14+
/// should sanity check by re-deriving the node pk and checking that it
15+
/// equals the client's expected value.
16+
pub node_pk: PublicKey,
17+
/// The secret root seed the client wants to provision into the node.
18+
pub root_seed: RootSeed,
19+
}
620

721
#[derive(Serialize, Deserialize)]
822
pub struct Node {

common/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// TODO
22

33
pub mod certs;
4+
pub mod provision;
45
pub mod tls;

common/src/client/provision.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// TODO
2+
3+
use anyhow::{Context, Result};
4+
use reqwest::{Client, Proxy};
5+
6+
use crate::api::provision::ProvisionRequest;
7+
use crate::api::UserPk;
8+
use crate::attest::EnclavePolicy;
9+
use crate::client::tls;
10+
11+
pub struct ProvisionClient {
12+
client: Client,
13+
provision_url: String,
14+
}
15+
16+
impl ProvisionClient {
17+
pub fn new(
18+
user_pk: &UserPk,
19+
proxy_url: &str,
20+
proxy_ca: &rustls::Certificate,
21+
provision_url: String,
22+
expect_dummy_quote: bool,
23+
enclave_policy: EnclavePolicy,
24+
) -> Result<Self> {
25+
// TODO(phlip9): actual auth in proxy header
26+
// TODO(phlip9): https only mode
27+
28+
let proxy = Proxy::https(proxy_url)
29+
.context("Invalid proxy url")?
30+
// TODO(phlip9): should be bearer auth
31+
.basic_auth(&user_pk.to_string(), "");
32+
33+
let tls = tls::client_provision_tls_config(
34+
proxy_ca,
35+
expect_dummy_quote,
36+
enclave_policy,
37+
)?;
38+
39+
let client = Client::builder()
40+
.proxy(proxy)
41+
.user_agent("lexe-provision-client")
42+
.use_preconfigured_tls(tls)
43+
.build()
44+
.context("Failed to build client")?;
45+
46+
Ok(Self {
47+
client,
48+
provision_url,
49+
})
50+
}
51+
52+
pub async fn provision(&self, req: ProvisionRequest) -> Result<()> {
53+
let provision_url = &self.provision_url;
54+
let url = format!("{provision_url}/provision");
55+
56+
let resp = self.client.post(url).json(&req).send().await?;
57+
58+
Ok(resp.error_for_status().map(|_| ())?)
59+
}
60+
}

node/src/provision.rs

Lines changed: 28 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ use std::time::{Duration, Instant};
2323

2424
use anyhow::{ensure, Context, Result};
2525
use bitcoin::secp256k1::PublicKey;
26-
use common::api::provision::{Instance, Node, NodeInstanceSeed, SealedSeed};
26+
use common::api::provision::{
27+
Instance, Node, NodeInstanceSeed, ProvisionRequest, SealedSeed,
28+
};
2729
use common::api::runner::UserPort;
2830
use common::api::UserPk;
2931
use common::attest::cert::AttestationCert;
@@ -34,7 +36,6 @@ use common::root_seed::RootSeed;
3436
use common::{ed25519, hex};
3537
use http::{Response, StatusCode};
3638
use secrecy::ExposeSecret;
37-
use serde::{Deserialize, Serialize};
3839
use thiserror::Error;
3940
use tokio::sync::mpsc;
4041
use tokio_rustls::rustls;
@@ -82,41 +83,6 @@ struct RequestContext {
8283
rng: SysRng,
8384
}
8485

85-
/// The client sends this provisioning request to the node.
86-
#[derive(Serialize, Deserialize)]
87-
struct ProvisionRequest {
88-
/// The client's user pk.
89-
user_pk: UserPk,
90-
/// The client's node public key, derived from the root seed. The node
91-
/// should sanity check by re-deriving the node pk and checking that it
92-
/// equals the client's expected value.
93-
node_pk: PublicKey,
94-
/// The secret root seed the client wants to provision into the node.
95-
root_seed: RootSeed,
96-
}
97-
98-
impl ProvisionRequest {
99-
fn verify<R: Crng>(
100-
self,
101-
rng: &mut R,
102-
expected_user_id: UserPk,
103-
) -> Result<(UserPk, PublicKey, ProvisionedSecrets)> {
104-
ensure!(self.user_pk == expected_user_id);
105-
106-
// TODO(phlip9): derive just the node pk without all the extra junk
107-
// that gets derived constructing a whole KeysManager
108-
let _keys_manager =
109-
LexeKeysManager::init(rng, &self.node_pk, &self.root_seed)?;
110-
Ok((
111-
self.user_pk,
112-
self.node_pk,
113-
ProvisionedSecrets {
114-
root_seed: self.root_seed,
115-
},
116-
))
117-
}
118-
}
119-
12086
/// The enclave's provisioned secrets that it will seal and persist using its
12187
/// platform enclave keys that are software and version specific.
12288
///
@@ -143,13 +109,33 @@ impl ProvisionedSecrets {
143109
}
144110
}
145111

112+
fn verify_provision_request<R: Crng>(
113+
rng: &mut R,
114+
expected_user_id: UserPk,
115+
req: ProvisionRequest,
116+
) -> Result<(UserPk, PublicKey, ProvisionedSecrets)> {
117+
ensure!(req.user_pk == expected_user_id);
118+
119+
// TODO(phlip9): derive just the node pk without all the extra junk
120+
// that gets derived constructing a whole KeysManager
121+
let _keys_manager =
122+
LexeKeysManager::init(rng, &req.node_pk, &req.root_seed)?;
123+
Ok((
124+
req.user_pk,
125+
req.node_pk,
126+
ProvisionedSecrets {
127+
root_seed: req.root_seed,
128+
},
129+
))
130+
}
131+
146132
// # provision service
147133
//
148134
// POST /provision
149135
//
150136
// ```json
151137
// {
152-
// "user_pk": UserPk::from_i64(123),
138+
// "user_pk": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
153139
// "node_pk": "031355a4419a2b31c9b1ba2de0bcbefdd4a2ef6360f2b018736162a9b3be329fd4".parse().unwrap(),
154140
// "root_seed": "86e4478f9f7e810d883f22ea2f0173e193904b488a62bb63764c82ba22b60ca7".parse().unwrap(),
155141
// }
@@ -160,9 +146,9 @@ async fn provision_request(
160146
) -> Result<impl Reply, ApiError> {
161147
debug!("received provision request");
162148

163-
let (user_pk, node_pk, provisioned_secrets) = req
164-
.verify(&mut ctx.rng, ctx.expected_user_id)
165-
.map_err(|_| ApiError)?;
149+
let (user_pk, node_pk, provisioned_secrets) =
150+
verify_provision_request(&mut ctx.rng, ctx.expected_user_id, req)
151+
.map_err(|_| ApiError)?;
166152

167153
let sealed_secrets = provisioned_secrets
168154
.seal(&mut ctx.rng)
@@ -198,7 +184,7 @@ async fn provision_request(
198184
// Provisioning done. Stop node.
199185
let _ = ctx.shutdown_tx.try_send(());
200186

201-
Ok("OK")
187+
Ok(Response::new(Body::empty()))
202188
}
203189

204190
fn provision_routes(

0 commit comments

Comments
 (0)