0767310e85
This patch changes from using biscuit to josekit for JWE. josekit uses OpenSSL for its crypto instead of ring. Signed-off-by: Patrick Uiterwijk <patrick@puiterwijk.org>
237 lines
8.6 KiB
Rust
237 lines
8.6 KiB
Rust
use std::convert::TryFrom;
|
|
|
|
use anyhow::{anyhow, bail, Error, Result};
|
|
use serde::{Deserialize, Serialize};
|
|
use tpm2_policy::TPMPolicyStep;
|
|
|
|
use crate::utils::get_authorized_policy_step;
|
|
|
|
#[derive(Serialize, Deserialize, std::fmt::Debug)]
|
|
pub(super) struct TPM2Config {
|
|
pub hash: Option<String>,
|
|
pub key: Option<String>,
|
|
pub pcr_bank: Option<String>,
|
|
// PCR IDs can be passed in as comma-separated string or json array
|
|
pub pcr_ids: Option<serde_json::Value>,
|
|
pub pcr_digest: Option<String>,
|
|
// Whether to use a policy. If this is specified without pubkey path or policy path, they get set to defaults
|
|
pub use_policy: Option<bool>,
|
|
// Public key (in JSON format) for a wildcard policy that's possibly OR'd with the PCR one
|
|
pub policy_pubkey_path: Option<String>,
|
|
pub policy_ref: Option<String>,
|
|
pub policy_path: Option<String>,
|
|
}
|
|
|
|
impl TryFrom<&TPM2Config> for TPMPolicyStep {
|
|
type Error = Error;
|
|
|
|
fn try_from(cfg: &TPM2Config) -> Result<Self> {
|
|
if cfg.pcr_ids.is_some() && cfg.policy_pubkey_path.is_some() {
|
|
Ok(TPMPolicyStep::Or([
|
|
Box::new(TPMPolicyStep::PCRs(
|
|
cfg.get_pcr_hash_alg(),
|
|
cfg.get_pcr_ids().unwrap(),
|
|
Box::new(TPMPolicyStep::NoStep),
|
|
)),
|
|
Box::new(get_authorized_policy_step(
|
|
cfg.policy_pubkey_path.as_ref().unwrap(),
|
|
&None,
|
|
&cfg.policy_ref,
|
|
)?),
|
|
Box::new(TPMPolicyStep::NoStep),
|
|
Box::new(TPMPolicyStep::NoStep),
|
|
Box::new(TPMPolicyStep::NoStep),
|
|
Box::new(TPMPolicyStep::NoStep),
|
|
Box::new(TPMPolicyStep::NoStep),
|
|
Box::new(TPMPolicyStep::NoStep),
|
|
]))
|
|
} else if cfg.pcr_ids.is_some() {
|
|
Ok(TPMPolicyStep::PCRs(
|
|
cfg.get_pcr_hash_alg(),
|
|
cfg.get_pcr_ids().unwrap(),
|
|
Box::new(TPMPolicyStep::NoStep),
|
|
))
|
|
} else if cfg.policy_pubkey_path.is_some() {
|
|
get_authorized_policy_step(
|
|
cfg.policy_pubkey_path.as_ref().unwrap(),
|
|
&None,
|
|
&cfg.policy_ref,
|
|
)
|
|
} else {
|
|
Ok(TPMPolicyStep::NoStep)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) const DEFAULT_POLICY_PATH: &str = "/boot/clevis_policy.json";
|
|
pub(crate) const DEFAULT_PUBKEY_PATH: &str = "/boot/clevis_pubkey.json";
|
|
pub(crate) const DEFAULT_POLICY_REF: &str = "";
|
|
|
|
impl TPM2Config {
|
|
pub(super) fn get_pcr_hash_alg(
|
|
&self,
|
|
) -> tss_esapi::interface_types::algorithm::HashingAlgorithm {
|
|
crate::utils::get_hash_alg_from_name(self.pcr_bank.as_ref())
|
|
}
|
|
|
|
pub(super) fn get_name_hash_alg(
|
|
&self,
|
|
) -> tss_esapi::interface_types::algorithm::HashingAlgorithm {
|
|
crate::utils::get_hash_alg_from_name(self.hash.as_ref())
|
|
}
|
|
|
|
pub(super) fn get_pcr_ids(&self) -> Option<Vec<u64>> {
|
|
match &self.pcr_ids {
|
|
None => None,
|
|
Some(serde_json::Value::Array(vals)) => {
|
|
Some(vals.iter().map(|x| x.as_u64().unwrap()).collect())
|
|
}
|
|
_ => panic!("Unexpected type found for pcr_ids"),
|
|
}
|
|
}
|
|
|
|
pub(super) fn get_pcr_ids_str(&self) -> Option<String> {
|
|
match &self.pcr_ids {
|
|
None => None,
|
|
Some(serde_json::Value::Array(vals)) => Some(
|
|
vals.iter()
|
|
.map(|x| x.as_u64().unwrap().to_string())
|
|
.collect::<Vec<String>>()
|
|
.join(","),
|
|
),
|
|
_ => panic!("Unexpected type found for pcr_ids"),
|
|
}
|
|
}
|
|
|
|
fn normalize(mut self) -> Result<TPM2Config> {
|
|
self.normalize_pcr_ids()?;
|
|
if self.pcr_ids.is_some() && self.pcr_bank.is_none() {
|
|
self.pcr_bank = Some("sha256".to_string());
|
|
}
|
|
// Make use of the defaults if not specified
|
|
if self.use_policy.is_some() && self.use_policy.unwrap() {
|
|
if self.policy_path.is_none() {
|
|
self.policy_path = Some(DEFAULT_POLICY_PATH.to_string());
|
|
}
|
|
if self.policy_pubkey_path.is_none() {
|
|
self.policy_pubkey_path = Some(DEFAULT_PUBKEY_PATH.to_string());
|
|
}
|
|
if self.policy_ref.is_none() {
|
|
self.policy_ref = Some(DEFAULT_POLICY_REF.to_string());
|
|
}
|
|
} else if self.policy_pubkey_path.is_some()
|
|
|| self.policy_path.is_some()
|
|
|| self.policy_ref.is_some()
|
|
{
|
|
eprintln!("To use a policy, please specifiy use_policy: true. Not specifying this will be a fatal error in a next release");
|
|
}
|
|
if (self.policy_pubkey_path.is_some()
|
|
|| self.policy_path.is_some()
|
|
|| self.policy_ref.is_some())
|
|
&& (self.policy_pubkey_path.is_none()
|
|
|| self.policy_path.is_none()
|
|
|| self.policy_ref.is_none())
|
|
{
|
|
bail!("Not all of policy pubkey, path and ref are specified",);
|
|
}
|
|
Ok(self)
|
|
}
|
|
|
|
fn normalize_pcr_ids(&mut self) -> Result<()> {
|
|
// Normalize from array with one string to just string
|
|
if let Some(serde_json::Value::Array(vals)) = &self.pcr_ids {
|
|
if vals.len() == 1 {
|
|
if let serde_json::Value::String(val) = &vals[0] {
|
|
self.pcr_ids = Some(serde_json::Value::String(val.to_string()));
|
|
}
|
|
}
|
|
}
|
|
// Normalize pcr_ids from comma-separated string to array
|
|
if let Some(serde_json::Value::String(val)) = &self.pcr_ids {
|
|
// Was a string, do a split
|
|
let newval: Vec<serde_json::Value> = val
|
|
.split(',')
|
|
.map(|x| serde_json::Value::String(x.trim().to_string()))
|
|
.collect();
|
|
self.pcr_ids = Some(serde_json::Value::Array(newval));
|
|
}
|
|
// Normalize pcr_ids from array of Strings to array of Numbers
|
|
if let Some(serde_json::Value::Array(vals)) = &self.pcr_ids {
|
|
let newvals: Result<Vec<serde_json::Value>, _> = vals
|
|
.iter()
|
|
.map(|x| match x {
|
|
serde_json::Value::String(val) => {
|
|
match val.trim().parse::<serde_json::Number>() {
|
|
Ok(res) => {
|
|
let new = serde_json::Value::Number(res);
|
|
if !new.is_u64() {
|
|
bail!("Non-positive string int");
|
|
}
|
|
Ok(new)
|
|
}
|
|
Err(_) => Err(anyhow!("Unparseable string int")),
|
|
}
|
|
}
|
|
serde_json::Value::Number(n) => {
|
|
let new = serde_json::Value::Number(n.clone());
|
|
if !new.is_u64() {
|
|
return Err(anyhow!("Non-positive int"));
|
|
}
|
|
Ok(new)
|
|
}
|
|
_ => Err(anyhow!("Invalid value in pcr_ids")),
|
|
})
|
|
.collect();
|
|
self.pcr_ids = Some(serde_json::Value::Array(newvals?));
|
|
}
|
|
|
|
match &self.pcr_ids {
|
|
None => Ok(()),
|
|
// The normalization above would've caught any non-ints
|
|
Some(serde_json::Value::Array(_)) => Ok(()),
|
|
_ => Err(anyhow!("Invalid type")),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(super) enum ActionMode {
|
|
Encrypt,
|
|
Decrypt,
|
|
Summary,
|
|
Help,
|
|
}
|
|
|
|
pub(super) fn get_mode_and_cfg(args: &[String]) -> Result<(ActionMode, Option<TPM2Config>)> {
|
|
if args.len() > 1 && args[1] == "--summary" {
|
|
return Ok((ActionMode::Summary, None));
|
|
}
|
|
if args.len() > 1 && args[1] == "--help" {
|
|
return Ok((ActionMode::Help, None));
|
|
}
|
|
if atty::is(atty::Stream::Stdin) {
|
|
return Ok((ActionMode::Help, None));
|
|
}
|
|
let (mode, cfgstr) = if args[0].contains("encrypt") && args.len() >= 2 {
|
|
(ActionMode::Encrypt, Some(&args[1]))
|
|
} else if args[0].contains("decrypt") {
|
|
(ActionMode::Decrypt, None)
|
|
} else if args.len() > 1 {
|
|
if args[1] == "encrypt" && args.len() >= 3 {
|
|
(ActionMode::Encrypt, Some(&args[2]))
|
|
} else if args[1] == "decrypt" {
|
|
(ActionMode::Decrypt, None)
|
|
} else {
|
|
bail!("No command specified");
|
|
}
|
|
} else {
|
|
bail!("No command specified");
|
|
};
|
|
|
|
let cfg: Option<TPM2Config> = match cfgstr {
|
|
None => None,
|
|
Some(cfgstr) => Some(serde_json::from_str::<TPM2Config>(cfgstr)?.normalize()?),
|
|
};
|
|
|
|
Ok((mode, cfg))
|
|
}
|