mod support;
use cratefield_module_sealed::record::CreateBlob;
use cratefield_testing::{FakeAuth, MemoryBlob, TestHarness, request_as};
use serde_json::Value;
use std::sync::Arc;
use support::{SUBJECT_ID, b64, prf_wrap, recovery_wrap};
const PLAINTEXT: &str = "sealed-fixture: the server must never see this line";
const PATH: &str = "/v1/sealed/blobs";
const BLOB_ID: &str = "fixture-blob-0001";
const PURPOSE: &str = "fixture.vault";
fn kit() -> TestHarness {
let auth: Arc<dyn cratefield_core::Auth> = Arc::new(FakeAuth::subjects());
TestHarness::with_ports(
vec![Box::new(cratefield_module_sealed::Sealed::new(
Arc::new(support::FakeKms::new()),
Arc::new(cratefield_module_sealed::NoopNotifier),
))],
move |ports| {
ports.auth = Some(Arc::clone(&auth));
ports.blob = Some(Arc::new(MemoryBlob::new()));
},
)
}
fn credential() -> String {
format!("fixture-{}", "credential")
}
fn client_record() -> CreateBlob {
let who = credential();
CreateBlob {
blob_id: BLOB_ID.to_owned(),
version: 1,
purpose: PURPOSE.to_owned(),
alg: "A256GCM".to_owned(),
ciphertext: b64(&[0x5E_u8; 64]),
wraps: vec![prf_wrap(&who), recovery_wrap("recovery-1")],
created_by_credential: who.clone(),
}
}
async fn all_server_bytes(kit: &TestHarness) -> Vec<Vec<u8>> {
let mut held = Vec::new();
for table in ["sealed_blobs", "sealed_deks", "sealed_audit"] {
let rows = kit
.db
.query(&cratefield_core::Statement::new(format!(
"SELECT * FROM {table}"
)))
.await
.expect("table scan");
for row in &rows.rows {
for (column, _) in row.columns() {
if let Some(text) = row.get::<String>(column) {
held.push(text.into_bytes());
}
if let Some(bytes) = row.get::<Vec<u8>>(column) {
held.push(bytes);
}
}
}
}
held
}
#[pollster::test]
async fn the_client_record_is_taken_verbatim_and_stays_opaque() {
let record = client_record();
let posted = serde_json::to_string(&record).expect("the record serialises");
let kit = kit();
let created = request_as(
&kit.router,
axum::http::Method::POST,
PATH,
SUBJECT_ID,
Some(&posted),
)
.await;
assert_eq!(
created.status,
axum::http::StatusCode::CREATED,
"{:?}",
created.body()
);
let served = request_as(
&kit.router,
axum::http::Method::GET,
&format!("{PATH}/{}", record.blob_id),
SUBJECT_ID,
None,
)
.await;
assert_eq!(served.status, axum::http::StatusCode::OK);
let body = served.json();
assert_eq!(body["ciphertext"], Value::String(record.ciphertext.clone()));
assert_eq!(
body["wraps"],
serde_json::to_value(&record.wraps).expect("wraps serialise")
);
assert_eq!(body["purpose"], record.purpose.as_str());
assert_eq!(body["alg"], record.alg.as_str());
assert_eq!(
body["created_by_credential"],
Value::String(record.created_by_credential.clone())
);
assert_eq!(body["subject"], SUBJECT_ID);
for held in all_server_bytes(&kit).await {
assert!(
!held
.windows(PLAINTEXT.len())
.any(|window| window == PLAINTEXT.as_bytes()),
"the server's bytes contain the fixture plaintext: {}",
String::from_utf8_lossy(&held)
);
}
}