mirror of https://github.com/tauri-apps/tauri
feat(bundler): support custom sign command on Windows (#9865)
* feat(bundler): support custom sign command on Windows closes #7188 closes #9578 * fix double quotes * fix build * fix build * clippy * Update sign.rs * clippy && replace `winreg` with `windows-registry` * remove log [skip ci] * Apply suggestions from code review * tweak arg so path with spaces work on macOS * create nsis toolset paths --------- Co-authored-by: Lucas Nogueira <lucas@tauri.app>
This commit is contained in:
parent
fc1543c65e
commit
d6d3efbd12
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"tauri-bundler": "patch:feat"
|
||||
---
|
||||
|
||||
On Windows, add option to specify a custom signing command to be used. This opens an endless possibilities, for example use `osslsigncode` on non-Windows or use hardware tokens and HSM or even using Azure Trusted Signing.
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"tauri-utils": "patch:feat"
|
||||
---
|
||||
|
||||
Add `sign_command` in `WindowsConfig`
|
|
@ -112,6 +112,7 @@
|
|||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": null,
|
||||
"nsis": null,
|
||||
"signCommand": null,
|
||||
"timestampUrl": null,
|
||||
"tsp": false,
|
||||
"webviewFixedRuntimePath": null,
|
||||
|
@ -1619,6 +1620,7 @@
|
|||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": null,
|
||||
"nsis": null,
|
||||
"signCommand": null,
|
||||
"timestampUrl": null,
|
||||
"tsp": false,
|
||||
"webviewFixedRuntimePath": null,
|
||||
|
@ -1977,6 +1979,13 @@
|
|||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"signCommand": {
|
||||
"description": "Specify a custom command to sign the binaries. This command needs to have a `%1` in it which is just a placeholder for the binary path, which we will detect and replace before calling the command.\n\nExample: ```text sign-cli --arg1 --arg2 %1 ```\n\nBy Default we use `signtool.exe` which can be found only on Windows so if you are on another platform and want to cross-compile and sign you will need to use another tool like `osslsigncode`.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
|
@ -859,6 +859,20 @@ pub struct WindowsConfig {
|
|||
pub wix: Option<WixConfig>,
|
||||
/// Configuration for the installer generated with NSIS.
|
||||
pub nsis: Option<NsisConfig>,
|
||||
/// Specify a custom command to sign the binaries.
|
||||
/// This command needs to have a `%1` in it which is just a placeholder for the binary path,
|
||||
/// which we will detect and replace before calling the command.
|
||||
///
|
||||
/// Example:
|
||||
/// ```text
|
||||
/// sign-cli --arg1 --arg2 %1
|
||||
/// ```
|
||||
///
|
||||
/// By Default we use `signtool.exe` which can be found only on Windows so
|
||||
/// if you are on another platform and want to cross-compile and sign you will
|
||||
/// need to use another tool like `osslsigncode`.
|
||||
#[serde(alias = "sign-command")]
|
||||
pub sign_command: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for WindowsConfig {
|
||||
|
@ -873,6 +887,7 @@ impl Default for WindowsConfig {
|
|||
allow_downgrades: true,
|
||||
wix: None,
|
||||
nsis: None,
|
||||
sign_command: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ dunce = "1"
|
|||
[target."cfg(target_os = \"windows\")".dependencies]
|
||||
uuid = { version = "1", features = [ "v4", "v5" ] }
|
||||
bitness = "0.4"
|
||||
winreg = "0.52"
|
||||
windows-registry = "0.1.1"
|
||||
glob = "0.3"
|
||||
|
||||
[target."cfg(target_os = \"windows\")".dependencies.windows-sys]
|
||||
|
|
|
@ -63,8 +63,7 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
|||
log::warn!("Cross-platform compilation is experimental and does not support all features. Please use a matching host system for full compatibility.");
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if settings.can_sign() {
|
||||
// Sign windows binaries before the bundling step in case neither wix and nsis bundles are enabled
|
||||
for bin in settings.binaries() {
|
||||
let bin_path = settings.binary_path(bin);
|
||||
|
@ -75,16 +74,24 @@ pub fn bundle_project(settings: Settings) -> crate::Result<Vec<Bundle>> {
|
|||
for bin in settings.external_binaries() {
|
||||
let path = bin?;
|
||||
let skip = std::env::var("TAURI_SKIP_SIDECAR_SIGNATURE_CHECK").map_or(false, |v| v == "true");
|
||||
if skip {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !skip && windows::sign::verify(&path)? {
|
||||
#[cfg(windows)]
|
||||
if windows::sign::verify(&path)? {
|
||||
log::info!(
|
||||
"sidecar at \"{}\" already signed. Skipping...",
|
||||
path.display()
|
||||
)
|
||||
} else {
|
||||
windows::sign::try_sign(&path, &settings)?;
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
windows::sign::try_sign(&path, &settings)?;
|
||||
}
|
||||
} else {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
log::warn!("Signing, by default, is only supported on Windows hosts, but you can specify a custom signing command in `bundler > windows > sign_command`, for now, skipping signing the installer...");
|
||||
}
|
||||
|
||||
for package_type in &package_types {
|
||||
|
|
|
@ -447,6 +447,20 @@ pub struct WindowsSettings {
|
|||
///
|
||||
/// /// The default value of this flag is `true`.
|
||||
pub allow_downgrades: bool,
|
||||
|
||||
/// Specify a custom command to sign the binaries.
|
||||
/// This command needs to have a `%1` in it which is just a placeholder for the binary path,
|
||||
/// which we will detect and replace before calling the command.
|
||||
///
|
||||
/// Example:
|
||||
/// ```text
|
||||
/// sign-cli --arg1 --arg2 %1
|
||||
/// ```
|
||||
///
|
||||
/// By Default we use `signtool.exe` which can be found only on Windows so
|
||||
/// if you are on another platform and want to cross-compile and sign you will
|
||||
/// need to use another tool like `osslsigncode`.
|
||||
pub sign_command: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for WindowsSettings {
|
||||
|
@ -462,6 +476,7 @@ impl Default for WindowsSettings {
|
|||
webview_install_mode: Default::default(),
|
||||
webview_fixed_runtime_path: None,
|
||||
allow_downgrades: true,
|
||||
sign_command: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#[cfg(target_os = "windows")]
|
||||
pub mod msi;
|
||||
pub mod nsis;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod sign;
|
||||
|
||||
mod util;
|
||||
|
|
|
@ -798,7 +798,11 @@ pub fn build_wix_app_installer(
|
|||
&msi_output_path,
|
||||
)?;
|
||||
rename(&msi_output_path, &msi_path)?;
|
||||
try_sign(&msi_path, settings)?;
|
||||
|
||||
if settings.can_sign() {
|
||||
try_sign(&msi_path, settings)?;
|
||||
}
|
||||
|
||||
output_paths.push(msi_path);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
use crate::bundle::windows::sign::{sign_command, try_sign};
|
||||
|
||||
use crate::{
|
||||
bundle::{
|
||||
common::CommandExt,
|
||||
|
@ -67,6 +67,7 @@ pub fn bundle_project(settings: &Settings, updater: bool) -> crate::Result<Vec<P
|
|||
let nsis_toolset_path = tauri_tools_path.join("NSIS");
|
||||
|
||||
if !nsis_toolset_path.exists() {
|
||||
create_dir_all(&nsis_toolset_path)?;
|
||||
get_and_extract_nsis(&nsis_toolset_path, &tauri_tools_path)?;
|
||||
} else if NSIS_REQUIRED_FILES
|
||||
.iter()
|
||||
|
@ -114,12 +115,10 @@ fn get_and_extract_nsis(nsis_toolset_path: &Path, _tauri_tools_path: &Path) -> c
|
|||
NSIS_TAURI_UTILS_SHA1,
|
||||
HashAlgorithm::Sha1,
|
||||
)?;
|
||||
write(
|
||||
nsis_plugins
|
||||
.join("x86-unicode")
|
||||
.join("nsis_tauri_utils.dll"),
|
||||
data,
|
||||
)?;
|
||||
|
||||
let target_folder = nsis_plugins.join("x86-unicode");
|
||||
create_dir_all(&target_folder)?;
|
||||
write(target_folder.join("nsis_tauri_utils.dll"), data)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -163,9 +162,6 @@ fn build_nsis_app_installer(
|
|||
|
||||
log::info!("Target: {}", arch);
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
log::info!("Code signing is currently only supported on Windows hosts, skipping...");
|
||||
|
||||
let output_path = settings.project_out_directory().join("nsis").join(arch);
|
||||
if output_path.exists() {
|
||||
remove_dir_all(&output_path)?;
|
||||
|
@ -197,16 +193,9 @@ fn build_nsis_app_installer(
|
|||
);
|
||||
data.insert("copyright", to_json(settings.copyright_string()));
|
||||
|
||||
// Code signing is currently only supported on Windows hosts
|
||||
#[cfg(target_os = "windows")]
|
||||
if settings.can_sign() {
|
||||
data.insert(
|
||||
"uninstaller_sign_cmd",
|
||||
to_json(format!(
|
||||
"{:?}",
|
||||
sign_command("%1", &settings.sign_params())?.0
|
||||
)),
|
||||
);
|
||||
let sign_cmd = format!("{:?}", sign_command("%1", &settings.sign_params())?);
|
||||
data.insert("uninstaller_sign_cmd", to_json(sign_cmd));
|
||||
}
|
||||
|
||||
let version = settings.version_string();
|
||||
|
@ -517,9 +506,12 @@ fn build_nsis_app_installer(
|
|||
|
||||
rename(nsis_output_path, &nsis_installer_path)?;
|
||||
|
||||
// Code signing is currently only supported on Windows hosts
|
||||
#[cfg(target_os = "windows")]
|
||||
try_sign(&nsis_installer_path, settings)?;
|
||||
if settings.can_sign() {
|
||||
try_sign(&nsis_installer_path, settings)?;
|
||||
} else {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
log::warn!("Signing, by default, is only supported on Windows hosts, but you can specify a custom signing command in `bundler > windows > sign_command`, for now, skipping signing the installer...");
|
||||
}
|
||||
|
||||
Ok(vec![nsis_installer_path])
|
||||
}
|
||||
|
|
|
@ -3,149 +3,21 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{
|
||||
bundle::{common::CommandExt, windows::util},
|
||||
Settings,
|
||||
};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
};
|
||||
use winreg::{
|
||||
enums::{HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_32KEY},
|
||||
RegKey,
|
||||
};
|
||||
|
||||
pub struct SignParams {
|
||||
pub product_name: String,
|
||||
pub digest_algorithm: String,
|
||||
pub certificate_thumbprint: String,
|
||||
pub timestamp_url: Option<String>,
|
||||
pub tsp: bool,
|
||||
}
|
||||
|
||||
// sign code forked from https://github.com/forbjok/rust-codesign
|
||||
fn locate_signtool() -> crate::Result<PathBuf> {
|
||||
const INSTALLED_ROOTS_REGKEY_PATH: &str = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
|
||||
const KITS_ROOT_REGVALUE_NAME: &str = r"KitsRoot10";
|
||||
|
||||
let installed_roots_key_path = Path::new(INSTALLED_ROOTS_REGKEY_PATH);
|
||||
|
||||
// Open 32-bit HKLM "Installed Roots" key
|
||||
let installed_roots_key = RegKey::predef(HKEY_LOCAL_MACHINE)
|
||||
.open_subkey_with_flags(installed_roots_key_path, KEY_READ | KEY_WOW64_32KEY)
|
||||
.map_err(|_| crate::Error::OpenRegistry(INSTALLED_ROOTS_REGKEY_PATH.to_string()))?;
|
||||
|
||||
// Get the Windows SDK root path
|
||||
let kits_root_10_path: String = installed_roots_key
|
||||
.get_value(KITS_ROOT_REGVALUE_NAME)
|
||||
.map_err(|_| crate::Error::GetRegistryValue(KITS_ROOT_REGVALUE_NAME.to_string()))?;
|
||||
|
||||
// Construct Windows SDK bin path
|
||||
let kits_root_10_bin_path = Path::new(&kits_root_10_path).join("bin");
|
||||
|
||||
let mut installed_kits: Vec<String> = installed_roots_key
|
||||
.enum_keys()
|
||||
/* Report and ignore errors, pass on values. */
|
||||
.filter_map(|res| match res {
|
||||
Ok(v) => Some(v),
|
||||
Err(_) => None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Sort installed kits
|
||||
installed_kits.sort();
|
||||
|
||||
/* Iterate through installed kit version keys in reverse (from newest to oldest),
|
||||
adding their bin paths to the list.
|
||||
Windows SDK 10 v10.0.15063.468 and later will have their signtools located there. */
|
||||
let mut kit_bin_paths: Vec<PathBuf> = installed_kits
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|kit| kits_root_10_bin_path.join(kit))
|
||||
.collect();
|
||||
|
||||
/* Add kits root bin path.
|
||||
For Windows SDK 10 versions earlier than v10.0.15063.468, signtool will be located there. */
|
||||
kit_bin_paths.push(kits_root_10_bin_path);
|
||||
|
||||
// Choose which version of SignTool to use based on OS bitness
|
||||
let arch_dir = util::os_bitness().ok_or(crate::Error::UnsupportedBitness)?;
|
||||
|
||||
/* Iterate through all bin paths, checking for existence of a SignTool executable. */
|
||||
for kit_bin_path in &kit_bin_paths {
|
||||
/* Construct SignTool path. */
|
||||
let signtool_path = kit_bin_path.join(arch_dir).join("signtool.exe");
|
||||
|
||||
/* Check if SignTool exists at this location. */
|
||||
if signtool_path.exists() {
|
||||
// SignTool found. Return it.
|
||||
return Ok(signtool_path);
|
||||
}
|
||||
}
|
||||
|
||||
Err(crate::Error::SignToolNotFound)
|
||||
}
|
||||
|
||||
/// Check if binary is already signed.
|
||||
/// Used to skip sidecar binaries that are already signed.
|
||||
pub fn verify(path: &Path) -> crate::Result<bool> {
|
||||
// Construct SignTool command
|
||||
let signtool = locate_signtool()?;
|
||||
|
||||
let mut cmd = Command::new(signtool);
|
||||
cmd.arg("verify");
|
||||
cmd.arg("/pa");
|
||||
cmd.arg(path);
|
||||
|
||||
Ok(cmd.status()?.success())
|
||||
}
|
||||
|
||||
pub fn sign_command(path: &str, params: &SignParams) -> crate::Result<(Command, PathBuf)> {
|
||||
// Construct SignTool command
|
||||
let signtool = locate_signtool()?;
|
||||
|
||||
let mut cmd = Command::new(&signtool);
|
||||
cmd.arg("sign");
|
||||
cmd.args(["/fd", ¶ms.digest_algorithm]);
|
||||
cmd.args(["/sha1", ¶ms.certificate_thumbprint]);
|
||||
cmd.args(["/d", ¶ms.product_name]);
|
||||
|
||||
if let Some(ref timestamp_url) = params.timestamp_url {
|
||||
if params.tsp {
|
||||
cmd.args(["/tr", timestamp_url]);
|
||||
cmd.args(["/td", ¶ms.digest_algorithm]);
|
||||
} else {
|
||||
cmd.args(["/t", timestamp_url]);
|
||||
}
|
||||
}
|
||||
|
||||
cmd.arg(path);
|
||||
|
||||
Ok((cmd, signtool))
|
||||
}
|
||||
|
||||
pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
|
||||
let path_str = path.as_ref().to_str().unwrap();
|
||||
|
||||
log::info!(action = "Signing"; "{} with identity \"{}\"", path_str, params.certificate_thumbprint);
|
||||
|
||||
let (mut cmd, signtool) = sign_command(path_str, params)?;
|
||||
log::debug!("Running signtool {:?}", signtool);
|
||||
|
||||
// Execute SignTool command
|
||||
let output = cmd.output_ok()?;
|
||||
|
||||
let stdout = String::from_utf8_lossy(output.stdout.as_slice()).into_owned();
|
||||
log::info!("{:?}", stdout);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(windows)]
|
||||
use crate::bundle::windows::util;
|
||||
use crate::{bundle::common::CommandExt, Settings};
|
||||
use anyhow::Context;
|
||||
#[cfg(windows)]
|
||||
use std::path::PathBuf;
|
||||
#[cfg(windows)]
|
||||
use std::sync::OnceLock;
|
||||
use std::{path::Path, process::Command};
|
||||
|
||||
impl Settings {
|
||||
pub(crate) fn can_sign(&self) -> bool {
|
||||
self.windows().certificate_thumbprint.is_some()
|
||||
self.windows().sign_command.is_some() || self.windows().certificate_thumbprint.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn sign_params(&self) -> SignParams {
|
||||
SignParams {
|
||||
product_name: self.product_name().into(),
|
||||
|
@ -166,10 +38,203 @@ impl Settings {
|
|||
.as_ref()
|
||||
.map(|url| url.to_string()),
|
||||
tsp: self.windows().tsp,
|
||||
sign_command: self.windows().sign_command.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SignParams {
|
||||
pub product_name: String,
|
||||
pub digest_algorithm: String,
|
||||
pub certificate_thumbprint: String,
|
||||
pub timestamp_url: Option<String>,
|
||||
pub tsp: bool,
|
||||
pub sign_command: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn signtool() -> Option<PathBuf> {
|
||||
// sign code forked from https://github.com/forbjok/rust-codesign
|
||||
static SIGN_TOOL: OnceLock<crate::Result<PathBuf>> = OnceLock::new();
|
||||
SIGN_TOOL
|
||||
.get_or_init(|| {
|
||||
const INSTALLED_ROOTS_REGKEY_PATH: &str = r"SOFTWARE\Microsoft\Windows Kits\Installed Roots";
|
||||
const KITS_ROOT_REGVALUE_NAME: &str = r"KitsRoot10";
|
||||
|
||||
// Open 32-bit HKLM "Installed Roots" key
|
||||
let installed_roots_key = windows_registry::LOCAL_MACHINE
|
||||
.open(INSTALLED_ROOTS_REGKEY_PATH)
|
||||
.map_err(|_| crate::Error::OpenRegistry(INSTALLED_ROOTS_REGKEY_PATH.to_string()))?;
|
||||
|
||||
// Get the Windows SDK root path
|
||||
let kits_root_10_path: String = installed_roots_key
|
||||
.get_string(KITS_ROOT_REGVALUE_NAME)
|
||||
.map_err(|_| crate::Error::GetRegistryValue(KITS_ROOT_REGVALUE_NAME.to_string()))?;
|
||||
|
||||
// Construct Windows SDK bin path
|
||||
let kits_root_10_bin_path = Path::new(&kits_root_10_path).join("bin");
|
||||
|
||||
let mut installed_kits: Vec<String> = installed_roots_key
|
||||
.keys()
|
||||
.map_err(|_| crate::Error::FailedToEnumerateRegKeys)?
|
||||
.collect();
|
||||
|
||||
// Sort installed kits
|
||||
installed_kits.sort();
|
||||
|
||||
/* Iterate through installed kit version keys in reverse (from newest to oldest),
|
||||
adding their bin paths to the list.
|
||||
Windows SDK 10 v10.0.15063.468 and later will have their signtools located there. */
|
||||
let mut kit_bin_paths: Vec<PathBuf> = installed_kits
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|kit| kits_root_10_bin_path.join(kit))
|
||||
.collect();
|
||||
|
||||
/* Add kits root bin path.
|
||||
For Windows SDK 10 versions earlier than v10.0.15063.468, signtool will be located there. */
|
||||
kit_bin_paths.push(kits_root_10_bin_path);
|
||||
|
||||
// Choose which version of SignTool to use based on OS bitness
|
||||
let arch_dir = util::os_bitness().ok_or(crate::Error::UnsupportedBitness)?;
|
||||
|
||||
/* Iterate through all bin paths, checking for existence of a SignTool executable. */
|
||||
for kit_bin_path in &kit_bin_paths {
|
||||
/* Construct SignTool path. */
|
||||
let signtool_path = kit_bin_path.join(arch_dir).join("signtool.exe");
|
||||
|
||||
/* Check if SignTool exists at this location. */
|
||||
if signtool_path.exists() {
|
||||
// SignTool found. Return it.
|
||||
return Ok(signtool_path);
|
||||
}
|
||||
}
|
||||
|
||||
Err(crate::Error::SignToolNotFound)
|
||||
})
|
||||
.as_ref()
|
||||
.ok()
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Check if binary is already signed.
|
||||
/// Used to skip sidecar binaries that are already signed.
|
||||
#[cfg(windows)]
|
||||
pub fn verify(path: &Path) -> crate::Result<bool> {
|
||||
let signtool = signtool().ok_or(crate::Error::SignToolNotFound)?;
|
||||
|
||||
let mut cmd = Command::new(signtool);
|
||||
cmd.arg("verify");
|
||||
cmd.arg("/pa");
|
||||
cmd.arg(path);
|
||||
|
||||
Ok(cmd.status()?.success())
|
||||
}
|
||||
|
||||
pub fn sign_command_custom<P: AsRef<Path>>(path: P, command: &str) -> crate::Result<Command> {
|
||||
let path = path.as_ref();
|
||||
|
||||
let mut args = command.trim().split(' ');
|
||||
let bin = args
|
||||
.next()
|
||||
.context("custom signing command doesn't contain a bin?")?;
|
||||
|
||||
let mut cmd = Command::new(bin);
|
||||
for arg in args {
|
||||
if arg == "%1" {
|
||||
cmd.arg(path);
|
||||
} else {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
}
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn sign_command_default<P: AsRef<Path>>(
|
||||
path: P,
|
||||
params: &SignParams,
|
||||
) -> crate::Result<Command> {
|
||||
let signtool = signtool().ok_or(crate::Error::SignToolNotFound)?;
|
||||
|
||||
let mut cmd = Command::new(&signtool);
|
||||
cmd.arg("sign");
|
||||
cmd.args(["/fd", ¶ms.digest_algorithm]);
|
||||
cmd.args(["/sha1", ¶ms.certificate_thumbprint]);
|
||||
cmd.args(["/d", ¶ms.product_name]);
|
||||
|
||||
if let Some(ref timestamp_url) = params.timestamp_url {
|
||||
if params.tsp {
|
||||
cmd.args(["/tr", timestamp_url]);
|
||||
cmd.args(["/td", ¶ms.digest_algorithm]);
|
||||
} else {
|
||||
cmd.args(["/t", timestamp_url]);
|
||||
}
|
||||
}
|
||||
|
||||
cmd.arg(path.as_ref());
|
||||
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
pub fn sign_command<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<Command> {
|
||||
match ¶ms.sign_command {
|
||||
Some(custom_command) => sign_command_custom(path, custom_command),
|
||||
#[cfg(windows)]
|
||||
None => sign_command_default(path, params),
|
||||
|
||||
// should not be reachable
|
||||
#[cfg(not(windows))]
|
||||
None => Ok(Command::new("")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sign_custom<P: AsRef<Path>>(path: P, custom_command: &str) -> crate::Result<()> {
|
||||
let path = path.as_ref();
|
||||
|
||||
log::info!(action = "Signing";"{} with a custom signing command", tauri_utils::display_path(path));
|
||||
|
||||
let mut cmd = sign_command_custom(path, custom_command)?;
|
||||
|
||||
let output = cmd.output_ok()?;
|
||||
|
||||
let stdout = String::from_utf8_lossy(output.stdout.as_slice()).into_owned();
|
||||
log::info!("{:?}", stdout);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn sign_default<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
|
||||
let signtool = signtool().ok_or(crate::Error::SignToolNotFound)?;
|
||||
let path = path.as_ref();
|
||||
|
||||
log::info!(action = "Signing"; "{} with identity \"{}\"", tauri_utils::display_path(path), params.certificate_thumbprint);
|
||||
|
||||
let mut cmd = sign_command_default(path, params)?;
|
||||
log::debug!("Running signtool {:?}", signtool);
|
||||
|
||||
// Execute SignTool command
|
||||
let output = cmd.output_ok()?;
|
||||
|
||||
let stdout = String::from_utf8_lossy(output.stdout.as_slice()).into_owned();
|
||||
log::info!("{:?}", stdout);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
|
||||
match ¶ms.sign_command {
|
||||
Some(custom_command) => sign_custom(path, custom_command),
|
||||
#[cfg(windows)]
|
||||
None => sign_default(path, params),
|
||||
// should not be reachable, as user should either use Windows
|
||||
// or specify a custom sign_command but we succeed anyways
|
||||
#[cfg(not(windows))]
|
||||
None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_sign(file_path: &std::path::PathBuf, settings: &Settings) -> crate::Result<()> {
|
||||
if settings.can_sign() {
|
||||
log::info!(action = "Signing"; "{}", tauri_utils::display_path(file_path));
|
||||
|
|
|
@ -94,6 +94,9 @@ pub enum Error {
|
|||
/// Failed to get registry value.
|
||||
#[error("failed to get {0} value on registry")]
|
||||
GetRegistryValue(String),
|
||||
/// Failed to enumerate registry keys.
|
||||
#[error("failed to enumerate registry keys")]
|
||||
FailedToEnumerateRegKeys,
|
||||
/// Unsupported OS bitness.
|
||||
#[error("unsupported OS bitness")]
|
||||
UnsupportedBitness,
|
||||
|
|
|
@ -4874,8 +4874,8 @@ dependencies = [
|
|||
"ureq",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
"windows-registry",
|
||||
"windows-sys 0.52.0",
|
||||
"winreg 0.52.0",
|
||||
"zip",
|
||||
]
|
||||
|
||||
|
@ -5952,6 +5952,16 @@ dependencies = [
|
|||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f721bc2e55efb506a1a395a545cb76c2481fb023d33b51f0050e7888716281cf"
|
||||
dependencies = [
|
||||
"windows-result",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.1.1"
|
||||
|
@ -6147,16 +6157,6 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winsafe"
|
||||
version = "0.0.19"
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": null,
|
||||
"nsis": null,
|
||||
"signCommand": null,
|
||||
"timestampUrl": null,
|
||||
"tsp": false,
|
||||
"webviewFixedRuntimePath": null,
|
||||
|
@ -1619,6 +1620,7 @@
|
|||
"certificateThumbprint": null,
|
||||
"digestAlgorithm": null,
|
||||
"nsis": null,
|
||||
"signCommand": null,
|
||||
"timestampUrl": null,
|
||||
"tsp": false,
|
||||
"webviewFixedRuntimePath": null,
|
||||
|
@ -1977,6 +1979,13 @@
|
|||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"signCommand": {
|
||||
"description": "Specify a custom command to sign the binaries. This command needs to have a `%1` in it which is just a placeholder for the binary path, which we will detect and replace before calling the command.\n\nExample: ```text sign-cli --arg1 --arg2 %1 ```\n\nBy Default we use `signtool.exe` which can be found only on Windows so if you are on another platform and want to cross-compile and sign you will need to use another tool like `osslsigncode`.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
|
|
@ -1420,6 +1420,7 @@ fn tauri_config_to_bundle_settings(
|
|||
webview_install_mode: config.windows.webview_install_mode,
|
||||
webview_fixed_runtime_path: config.windows.webview_fixed_runtime_path,
|
||||
allow_downgrades: config.windows.allow_downgrades,
|
||||
sign_command: config.windows.sign_command,
|
||||
},
|
||||
license: config.license.or_else(|| {
|
||||
settings
|
||||
|
|
Loading…
Reference in New Issue