pub mod apk;
pub mod apt_package_list;
pub mod apt_release;
pub mod pacman;
use crate::args::Tamper;
use crate::artifacts::git;
use crate::artifacts::git::Oid;
use crate::errors::*;
use crate::keygen::EmbeddedKey;
use crate::keygen::openssl::OpensslEmbedded;
use crate::keygen::pgp::PgpEmbedded;
use crate::plot::{PatchAptReleaseConfig, PatchPkgDatabaseConfig, PlotExtras};
use crate::tamper;
use bstr::BString;
use std::collections::BTreeMap;
use std::fs;
use std::fs::File;
use std::io;
use std::io::{Read, Write};
pub async fn run(tamper: Tamper) -> Result<()> {
match tamper {
Tamper::PacmanDb(tamper) => {
let db = fs::read(&tamper.path)?;
let mut out = File::create(&tamper.out)?;
let config = PatchPkgDatabaseConfig::<Vec<String>>::from_args(tamper.config)?;
let plot_extras = PlotExtras::default();
tamper::pacman::patch(&config, &plot_extras, &db, &mut out)?;
}
Tamper::AptRelease(tamper) => {
let db = fs::read(&tamper.path)?;
let mut out = File::create(&tamper.out)?;
let checksum_config = PatchPkgDatabaseConfig::<String>::from_args(tamper.config)?;
let mut release_fields = BTreeMap::new();
for s in &tamper.release_set {
let (key, value) = s.split_once('=').context("Argument is not an assignment")?;
release_fields.insert(key.to_string(), value.to_string());
}
let signing_key = match (tamper.unsigned, tamper.signing_key) {
(true, Some(_)) => {
warn!(
"Using --unsigned and --signing-key together is causing the release to be unsigned"
);
None
}
(true, None) => None,
(false, Some(path)) => Some(PgpEmbedded::read_from_disk(path)?),
(false, None) => bail!("Missing --signing-key and --unsigned wasn't provided"),
};
let mut plot_extras = PlotExtras::default();
let signing_key = if let Some(signing_key) = signing_key {
plot_extras
.signing_keys
.insert("pgp".to_string(), EmbeddedKey::Pgp(signing_key));
Some("pgp".to_string())
} else {
None
};
let config = PatchAptReleaseConfig {
fields: release_fields,
checksums: checksum_config,
signing_key,
};
tamper::apt_release::patch(
&config,
&plot_extras.artifacts,
&plot_extras.signing_keys,
&db,
&mut out,
)?;
}
Tamper::AptPackageList(tamper) => {
let db = fs::read(&tamper.path)?;
let mut out = File::create(&tamper.out)?;
let plot_extras = PlotExtras::default();
let config = PatchPkgDatabaseConfig::<Vec<String>>::from_args(tamper.config)?;
tamper::apt_package_list::patch(&config, None, &plot_extras.artifacts, &db, &mut out)?;
}
Tamper::ApkIndex(tamper) => {
let db = fs::read(&tamper.path)?;
let mut out = File::create(&tamper.out)?;
let signing_key = OpensslEmbedded::read_from_disk(tamper.signing_key)?;
let mut plot_extras = PlotExtras::default();
plot_extras
.signing_keys
.insert("openssl".to_string(), EmbeddedKey::Openssl(signing_key));
let config = PatchPkgDatabaseConfig::<String>::from_args(tamper.config)?;
tamper::apk::patch(
&config,
&plot_extras,
"openssl",
&tamper.signing_key_name,
&db,
&mut out,
)?;
}
Tamper::GitCommit(tamper) => {
let mut out = Vec::new();
let mut tree = None;
let mut parents = Vec::new();
let mut author = None;
let mut committer = None;
let mut extra_headers = Vec::new();
let mut message = None;
if tamper.stdin {
let mut buf = Vec::new();
io::stdin()
.read_to_end(&mut buf)
.context("Failed to read commit from stdin")?;
let commit = gix_object::CommitRef::from_bytes(&buf)
.context("Failed to parse stdin as commit")?;
tree = Some(git_oid_to_string(commit.tree)?);
parents = commit
.parents
.into_iter()
.map(git_oid_to_string)
.collect::<Result<_>>()?;
author = Some(git_author_to_string(&commit.author)?);
committer = Some(git_author_to_string(&commit.committer)?);
extra_headers = commit
.extra_headers
.into_iter()
.map(|(k, v)| (k.into(), v.into_owned()))
.collect();
message = Some(commit.message.into());
}
if let Some(value) = tamper.tree {
tree = Some(value);
}
if !tamper.parents.is_empty() {
parents = tamper.parents;
}
if tamper.no_parents {
parents.clear();
}
if let Some(value) = tamper.author {
author = Some(value);
}
if let Some(value) = tamper.committer {
committer = Some(value);
}
if let Some(mut value) = tamper.message {
value.push('\n');
message = Some(BString::new(value.into()));
}
if tamper.message_stdin {
let mut buf = Vec::new();
io::stdin().read_to_end(&mut buf)?;
message = Some(BString::new(buf));
}
let commit = git::Commit {
tree: Oid::Inline(tree.context("Missing value for tree")?),
parents: parents.into_iter().map(Oid::Inline).collect(),
author: author.context("Missing value for git author")?,
committer: committer.context("Missing value for git committer")?,
extra_headers,
message: message.context("Missing message for git commit")?,
collision_prefix: tamper.collision_prefix,
nonce: tamper.nonce,
};
commit.encode(&mut out, &Default::default()).await?;
let out = if tamper.strip_header {
let (_, _, consumed) = gix_object::decode::loose_header(&out)?;
&out[consumed..]
} else {
&out[..]
};
io::stdout().write_all(out)?;
}
}
Ok(())
}
pub fn git_oid_to_string(oid: &bstr::BStr) -> Result<String> {
let s = std::str::from_utf8(oid)?;
Ok(s.to_string())
}
pub fn git_author_to_string(author: &gix_actor::SignatureRef) -> Result<String> {
let mut buf = Vec::new();
author.write_to(&mut buf)?;
let buf = String::from_utf8(buf)?;
Ok(buf)
}