Skip to content

Commit

Permalink
feat: verify zig with minisign (#3793)
Browse files Browse the repository at this point in the history
* feat: verify zig with minisign

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
  • Loading branch information
jdx and autofix-ci[bot] authored Dec 23, 2024
1 parent 13c9cb5 commit de66eae
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 20 deletions.
4 changes: 4 additions & 0 deletions e2e/core/test_zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

assert "mise x zig@0.13.0 -- zig version" "0.13.0"
assert "mise x zig@ref:master -- zig version"
13 changes: 6 additions & 7 deletions e2e/generate/test_generate_bootstrap
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
#!/usr/bin/env bash

# TODO: enable this after next release
#assert "mise generate bootstrap -w"
#assert "./bin/mise -v" "xx"
#
#assert "mise task add xxx -- echo 'running xxx'"
#assert "mise generate task-stubs --mise-bin ./bin/mise"
#assert "./bin/xxx" "running xxx"
assert "mise generate bootstrap -w"
assert "./bin/mise version"

assert "mise task add xxx -- echo 'running xxx'"
assert "mise generate task-stubs --mise-bin ./bin/mise"
assert "./bin/xxx" "running xxx"
5 changes: 5 additions & 0 deletions schema/mise.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@
"description": "Use cosign to verify aqua tool signatures.",
"type": "boolean"
},
"minisign": {
"default": true,
"description": "Use minisign to verify aqua tool signatures.",
"type": "boolean"
},
"registry_url": {
"description": "URL to fetch aqua registry from.",
"type": "string"
Expand Down
6 changes: 6 additions & 0 deletions settings.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ type = "Bool"
default = true
description = "Use cosign to verify aqua tool signatures."

[aqua.minisign]
env = "MISE_AQUA_MINISIGN"
type = "Bool"
default = true
description = "Use minisign to verify aqua tool signatures."

[aqua.registry_url]
env = "MISE_AQUA_REGISTRY_URL"
type = "Url"
Expand Down
38 changes: 38 additions & 0 deletions src/aqua/aqua_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ pub struct AquaPackage {
pub version_source: Option<String>,
pub checksum: Option<AquaChecksum>,
pub slsa_provenance: Option<AquaSlsaProvenance>,
pub minisign: Option<AquaMinisign>,
overrides: Vec<AquaOverride>,
version_constraint: String,
version_overrides: Vec<AquaPackage>,
Expand Down Expand Up @@ -131,6 +132,14 @@ pub struct AquaSlsaProvenance {
pub asset: Option<String>,
}

#[derive(Debug, Deserialize, Clone)]
pub struct AquaMinisign {
pub enabled: Option<bool>,
pub r#type: String,
pub url: String,
pub public_key: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct AquaChecksum {
pub r#type: Option<AquaChecksumType>,
Expand Down Expand Up @@ -483,6 +492,11 @@ fn apply_override(mut orig: AquaPackage, avo: &AquaPackage) -> AquaPackage {
slsa_provenance.merge(avo_slsa_provenance);
orig.slsa_provenance = Some(slsa_provenance);
}
if let Some(avo_minisign) = avo.minisign.clone() {
let mut minisign = orig.minisign.unwrap_or_else(|| avo_minisign.clone());
minisign.merge(avo_minisign);
orig.minisign = Some(minisign);
}
orig
}

Expand Down Expand Up @@ -670,3 +684,27 @@ impl AquaSlsaProvenance {
}
}
}

impl AquaMinisign {
pub fn url(&self, pkg: &AquaPackage, v: &str) -> Result<String> {
pkg.parse_aqua_str(&self.url, v, &Default::default())
}
pub fn public_key(&self, pkg: &AquaPackage, v: &str) -> Result<String> {
pkg.parse_aqua_str(&self.public_key, v, &Default::default())
}

fn merge(&mut self, other: Self) {
if let Some(enabled) = other.enabled {
self.enabled = Some(enabled);
}
if !other.r#type.is_empty() {
self.r#type = other.r#type;
}
if !other.url.is_empty() {
self.url = other.url;
}
if !other.public_key.is_empty() {
self.public_key = other.public_key;
}
}
}
39 changes: 38 additions & 1 deletion src/backend/aqua.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::install_context::InstallContext;
use crate::plugins::VERSION_REGEX;
use crate::registry::REGISTRY;
use crate::toolset::ToolVersion;
use crate::{file, github};
use crate::{file, github, minisign};
use eyre::{bail, ContextCompat, Result};
use indexmap::IndexSet;
use itertools::Itertools;
Expand Down Expand Up @@ -244,6 +244,7 @@ impl AquaBackend {
filename: &str,
) -> Result<()> {
self.verify_slsa(ctx, tv, pkg, v, filename)?;
self.verify_minisign(ctx, tv, pkg, v, filename)?;
if !tv.checksums.contains_key(filename) {
if let Some(checksum) = &pkg.checksum {
if checksum.enabled() {
Expand Down Expand Up @@ -315,6 +316,42 @@ impl AquaBackend {
Ok(())
}

fn verify_minisign(
&self,
ctx: &InstallContext,
tv: &mut ToolVersion,
pkg: &AquaPackage,
v: &str,
filename: &str,
) -> Result<()> {
if !SETTINGS.aqua.slsa {
return Ok(());
}
if let Some(minisign) = &pkg.minisign {
if minisign.enabled == Some(false) {
debug!("minisign is disabled for {tv}");
return Ok(());
}
ctx.pr.set_message("verify minisign".to_string());
let sig_path = match minisign.r#type.as_str() {
"http" => {
let url = minisign.url(pkg, v)?;
let path = tv.download_path().join(filename).with_extension(".minisig");
HTTP.download_file(&url, &path, Some(&ctx.pr))?;
path
}
t => {
warn!("unsupported minisign type: {t}");
return Ok(());
}
};
let data = file::read(tv.download_path().join(filename))?;
let sig = file::read_to_string(sig_path)?;
minisign::verify(&minisign.public_key(pkg, v)?, &data, &sig)?;
}
Ok(())
}

fn verify_slsa(
&self,
ctx: &InstallContext,
Expand Down
8 changes: 4 additions & 4 deletions src/cli/generate/bootstrap.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::config::SETTINGS;
use crate::file;
use crate::http::HTTP;
use crate::ui::info;
use crate::Result;
use crate::{file, minisign};
use clap::ValueHint;
use std::path::PathBuf;
use xx::file::display_path;
Expand Down Expand Up @@ -54,8 +54,8 @@ impl Bootstrap {
"https://mise.jdx.dev/install.sh".into()
};
let install = HTTP.get_text(&url)?;
// let install_sig = HTTP.get_text(format!("{url}.minisig"))?;
// minisign::verify(minisign::MISE_PUB_KEY, &install, &install_sig)?;
let install_sig = HTTP.get_text(format!("{url}.minisig"))?;
minisign::verify(&minisign::MISE_PUB_KEY, install.as_bytes(), &install_sig)?;
let install = info::indent_by(install, " ");
let version = regex!(r#"version="\$\{MISE_VERSION:-v([0-9.]+)\}""#)
.captures(&install)
Expand Down Expand Up @@ -90,7 +90,7 @@ export MISE_INSTALL_PATH="$cache_home/mise-{version}"
let script = format!(
r#"
#!/bin/sh
set -euxo pipefail
set -eu
__mise_bootstrap() {{
{vars}
Expand Down
3 changes: 2 additions & 1 deletion src/cli/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ pub fn print_version_if_requested(args: &[String]) -> std::io::Result<bool> {
let mise_bin = "mise";
#[cfg(windows)]
let mise_bin = "mise.exe";
if args.len() == 2 && *env::MISE_BIN_NAME == mise_bin {
if args.len() == 2 && *env::MISE_BIN_NAME == mise_bin || env::MISE_BIN_NAME.starts_with("mise-")
{
let cmd = &args[1].to_lowercase();
if cmd == "version" || cmd == "-v" || cmd == "--version" || cmd == "v" {
show_version()?;
Expand Down
1 change: 0 additions & 1 deletion src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ pub fn open<P: AsRef<Path>>(path: P) -> Result<File> {
File::open(path).wrap_err_with(|| format!("failed open: {}", display_path(path)))
}

#[allow(unused)]
pub fn read<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
let path = path.as_ref();
trace!("cat {}", display_path(path));
Expand Down
18 changes: 13 additions & 5 deletions src/minisign.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
use crate::*;
use minisign_verify::*;
#[allow(dead_code)]
pub const MISE_PUB_KEY: &str = include_str!("../minisign.pub");
use std::iter::Iterator;
use std::sync::LazyLock;

#[allow(dead_code)]
pub fn verify(pub_key: &str, data: &str, sig: &str) -> Result<()> {
pub static MISE_PUB_KEY: LazyLock<String> = LazyLock::new(|| {
include_str!("../minisign.pub")
.to_string()
.lines()
.last()
.unwrap()
.to_string()
});

pub fn verify(pub_key: &str, data: &[u8], sig: &str) -> Result<()> {
let public_key = PublicKey::from_base64(pub_key)?;
let signature = Signature::decode(sig)?;
public_key.verify(data.as_bytes(), &signature, false)?;
public_key.verify(data, &signature, false)?;
Ok(())
}
9 changes: 8 additions & 1 deletion src/plugins/core/zig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::http::{HTTP, HTTP_FETCH};
use crate::install_context::InstallContext;
use crate::toolset::{ToolRequest, ToolVersion};
use crate::ui::progress_report::SingleReport;
use crate::{file, github, plugins};
use crate::{file, github, minisign, plugins};
use contracts::requires;
use eyre::Result;
use itertools::Itertools;
Expand All @@ -22,6 +22,8 @@ pub struct ZigPlugin {
ba: BackendArg,
}

const ZIG_MINISIGN_KEY: &str = "RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U";

impl ZigPlugin {
pub fn new() -> Self {
Self {
Expand Down Expand Up @@ -81,6 +83,11 @@ impl ZigPlugin {
pr.set_message(format!("download {filename}"));
HTTP.download_file(&url, &tarball_path, Some(pr))?;

pr.set_message(format!("minisign {filename}"));
let tarball_data = file::read(&tarball_path)?;
let sig = HTTP.get_text(format!("{url}.minisig"))?;
minisign::verify(ZIG_MINISIGN_KEY, &tarball_data, &sig)?;

Ok(tarball_path)
}

Expand Down

0 comments on commit de66eae

Please sign in to comment.