Add prototype encrypted CAS blobs

This commit is contained in:
Eric Wendland 2026-05-21 01:35:00 +02:00
commit 533ffc8c2a
8 changed files with 382 additions and 6 deletions

View file

@ -4509,6 +4509,48 @@ pub fn handle_request(
size_bytes: info.size_bytes,
})
}
ControlRequest::CasAddPrivate { resource, path } => {
ensure_resource_exists(&store, &resource)?;
let secret = store.latest_resource_secret(&resource)?.ok_or_else(|| {
NodeError::IrohPeer(format!(
"resource {resource} has no active resource secret; run geth secret create {resource}"
))
})?;
let plaintext = std::fs::read(&path)?;
let plaintext_hash = geth_cas::hash_bytes(&plaintext);
let nonce = geth_crypto::blake3_hex(
format!(
"{}\0{}\0{}\0{}",
resource,
secret.secret_id,
secret.epoch,
geth_store::now_ms()
)
.as_bytes(),
);
let encrypted = geth_cas::encrypt_private_blob(
&resource,
&secret.secret_id,
secret.epoch,
&nonce,
&plaintext,
)?;
let cas = LocalCas::new(node.paths.cas_dir());
let info = cas.add_bytes(&encrypted)?;
store.record_cas_object(
info.hash.as_str(),
info.size_bytes,
&info.path.to_string_lossy(),
)?;
Ok(ControlResponse::CasPrivateAdded {
resource,
epoch: secret.epoch,
plaintext_hash,
encrypted_hash: info.hash,
size_bytes: info.size_bytes,
note: "stored prototype encrypted private blob envelope; no forward secrecy or post-compromise security is claimed".to_owned(),
})
}
ControlRequest::CasGet { hash, out } => {
let cas = LocalCas::new(node.paths.cas_dir());
let size_bytes = cas.get_to_path(&hash, &out)?;
@ -4518,6 +4560,51 @@ pub fn handle_request(
size_bytes,
})
}
ControlRequest::CasGetPrivate {
resource,
hash,
out,
} => {
ensure_resource_exists(&store, &resource)?;
let cas = LocalCas::new(node.paths.cas_dir());
let encrypted = cas.read_bytes(&hash)?;
let secrets = store
.list_resource_secrets()?
.into_iter()
.filter(|secret| secret.resource_id == resource)
.collect::<Vec<_>>();
if secrets.is_empty() {
return Err(NodeError::IrohPeer(format!(
"resource {resource} has no resource secrets; cannot decrypt private blob"
)));
}
let mut plaintext = None;
for secret in secrets {
if let Ok(bytes) =
geth_cas::decrypt_private_blob(&resource, &secret.secret_id, &encrypted)
{
plaintext = Some(bytes);
break;
}
}
let plaintext = plaintext.ok_or_else(|| {
NodeError::IrohPeer(format!(
"no local resource secret epoch could decrypt private blob {hash}"
))
})?;
if let Some(parent) = out.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(&out, &plaintext)?;
Ok(ControlResponse::CasPrivateGot {
resource,
hash,
plaintext_hash: geth_cas::hash_bytes(&plaintext),
out,
size_bytes: plaintext.len() as u64,
note: "decrypted prototype private blob envelope with the latest local resource secret epoch".to_owned(),
})
}
ControlRequest::CasHash { path } => Ok(ControlResponse::CasHash {
hash: hash_path(&path)?,
}),