use crate::cas;
use crate::error::{Error, Result};
use crate::etree::TextNode;
use crate::etree::TextTree;
use crate::password;
use crate::prot;
pub fn transform(text_in: &TextTree, paops: &mut crate::etree::ParseOps) -> Result<TextTree> {
if paops.max_depth != 0 && paops.runtime.level > paops.max_depth {
return Err(Error::Msg("Maximum recursion depth!".into()));
}
let mut out = Vec::with_capacity(text_in.len());
for node in text_in {
let new_node = match node {
TextNode::Plain(_) | TextNode::Data(_) => node.clone(),
TextNode::Chain { .. } | TextNode::Include { .. } | TextNode::Conflict { .. } => {
node.clone()
}
TextNode::BeginEnd { keyw, txt } => transform_begin_end(keyw, txt, paops)?,
TextNode::Encrypted {
keyw,
txt,
extfields,
} => transform_encrypted(keyw, txt, extfields, paops)?,
TextNode::Stored { keyw, cas } => transform_stored(keyw, cas, paops)?,
};
out.push(new_node);
}
Ok(out)
}
fn transform_begin_end(
keyw: &str,
txt: &TextTree,
paops: &mut crate::etree::ParseOps,
) -> Result<TextNode> {
if paops.transforms.encrypt.contains(keyw) {
paops.runtime.level += 1;
let block = transform(txt, paops)?;
paops.runtime.level -= 1;
let pt = crate::etree::tree_to_blob(&block, paops)?;
let (ct, extfields) = if !paops.crypto.recipient_pubs.is_empty() {
let rng = paops
.crypto
.rng
.as_mut()
.ok_or(Error::Msg("Missing RNG for KEM encrypt".into()))?;
crate::kemenc::encrypt(
pt,
&paops.crypto.recipient_pubs,
&paops.crypto.cipheropts.alg,
rng,
)?
} else {
let pass = ensure_password(keyw, paops, true);
prot::encrypt(
pt,
&pass,
&mut paops.crypto.rng,
&paops.crypto.pbkdfopts,
&paops.crypto.cipheropts,
&mut paops.crypto.pbkdf_cache,
&*paops.crypto.policy,
)?
};
let inner = if paops.transforms.store.contains(keyw) || cas_default_applies(paops) {
let hexhash = cas::save(ct, paops)?;
vec![TextNode::Stored {
keyw: "ct".to_string(),
cas: hexhash,
}]
} else {
vec![TextNode::Data(ct)]
};
return Ok(TextNode::Encrypted {
keyw: keyw.to_string(),
txt: inner,
extfields,
});
}
if paops.transforms.store.contains(keyw) {
paops.runtime.level += 1;
let block = transform(txt, paops)?;
paops.runtime.level -= 1;
let blob = crate::etree::tree_to_blob(&block, paops)?;
let hexhash = cas::save(blob, paops)?;
return Ok(TextNode::Stored {
keyw: keyw.to_string(),
cas: hexhash,
});
};
paops.runtime.level += 1;
let block = transform(txt, paops)?;
paops.runtime.level -= 1;
Ok(TextNode::BeginEnd {
keyw: keyw.to_string(),
txt: block,
})
}
fn transform_encrypted(
keyw: &str,
txt: &TextTree,
extfields: &std::collections::BTreeMap<String, String>,
paops: &mut crate::etree::ParseOps,
) -> Result<TextNode> {
if paops.transforms.decrypt.contains(keyw) {
let ct = match &txt[0] {
TextNode::Data(data) => data.clone(),
TextNode::Stored { cas: hexhash, .. } => cas::load(hexhash, paops)?,
_ => return Err(Error::Msg("No data in ENCRYPTED.".into())),
};
let pt = if extfields.contains_key("recipients") {
let priv_pem = paops
.crypto
.recipient_privkeys
.get(keyw)
.or_else(|| paops.crypto.recipient_privkeys.values().next())
.ok_or_else(|| {
Error::Msg(format!(
"KEM-mode block for WORD {} but no --key-file privkey supplied",
keyw
))
})?;
crate::kemenc::decrypt(&ct, priv_pem, extfields)?
} else {
let pass = ensure_password(keyw, paops, false);
match prot::decrypt(
ct,
&pass,
&extfields.get("pbkdf"),
&extfields.get("cipher"),
&mut paops.crypto.pbkdf_cache,
&*paops.crypto.policy,
) {
Ok(ct) => ct.to_vec(),
Err(e) => {
eprintln!("Error decrypting {}: {}.", keyw, e);
return Err(e);
}
}
};
let mut block = crate::etree::blob_to_tree(pt, "decrypted".to_string(), paops)?;
paops.runtime.level += 1;
block = transform(&block, paops)?;
paops.runtime.level -= 1;
return Ok(TextNode::BeginEnd {
keyw: keyw.to_string(),
txt: block,
});
}
if paops.transforms.store.contains(keyw) {
let hexhash = match &txt[0] {
TextNode::Data(data) => cas::save(data.clone(), paops)?,
TextNode::Stored { cas: hexhash, .. } => hexhash.clone(),
_ => return Err(Error::Msg("No data in ENCRYPTED.".into())),
};
return Ok(TextNode::Encrypted {
keyw: keyw.to_string(),
txt: vec![TextNode::Stored {
keyw: "ct".to_string(),
cas: hexhash,
}],
extfields: std::collections::BTreeMap::new(),
});
}
if paops.transforms.fetch.contains(keyw) {
let ct = match &txt[0] {
TextNode::Data(data) => data.clone(),
TextNode::Stored { cas: hexhash, .. } => cas::load(hexhash, paops)?,
_ => return Err(Error::Msg("No data in ENCRYPTED.".into())),
};
return Ok(TextNode::Encrypted {
keyw: keyw.to_string(),
txt: vec![TextNode::Data(ct)],
extfields: std::collections::BTreeMap::new(),
});
}
Ok(TextNode::Encrypted {
keyw: keyw.to_string(),
txt: txt.clone(),
extfields: extfields.clone(),
})
}
fn transform_stored(keyw: &str, cas: &str, paops: &mut crate::etree::ParseOps) -> Result<TextNode> {
if paops.transforms.fetch.contains(keyw) {
let blob = cas::load(cas, paops)?;
let mut block = crate::etree::blob_to_tree(blob, cas.to_string(), paops)?;
paops.runtime.level += 1;
block = transform(&block, paops)?;
paops.runtime.level -= 1;
return Ok(TextNode::BeginEnd {
keyw: keyw.to_string(),
txt: block,
});
}
Ok(TextNode::Stored {
keyw: keyw.to_string(),
cas: cas.to_string(),
})
}
fn ensure_password(keyw: &str, paops: &mut crate::etree::ParseOps, repeat: bool) -> String {
if let Some(p) = paops.passwords.get(keyw) {
return p.clone();
}
let p = password::get_password(keyw, repeat);
paops.passwords.insert(keyw.to_string(), p.clone());
p
}
fn cas_default_applies(paops: &crate::etree::ParseOps) -> bool {
!paops.io.inline_data
}