Skip to content

Commit 764968a

Browse files
authored
feat(bundler/nsis): sign uninstaller, closes #7348 (#7398)
* feat(bundler/nsis): sign uninstaller, closes #7348 * Update bundler-nsis-sign-uninstaller.md * clippy
1 parent 757e959 commit 764968a

File tree

6 files changed

+82
-50
lines changed

6 files changed

+82
-50
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'tauri-bundler': 'minor:enhance'
3+
---
4+
5+
Sign NSIS uninstaller as well.

tooling/bundler/src/bundle/windows/msi/wix.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ use crate::bundle::{
77
common::CommandExt,
88
path_utils::{copy_file, FileOpts},
99
settings::Settings,
10-
windows::util::{
11-
download, download_and_verify, extract_zip, try_sign, HashAlgorithm, WEBVIEW2_BOOTSTRAPPER_URL,
12-
WEBVIEW2_X64_INSTALLER_GUID, WEBVIEW2_X86_INSTALLER_GUID, WIX_OUTPUT_FOLDER_NAME,
13-
WIX_UPDATER_OUTPUT_FOLDER_NAME,
10+
windows::{
11+
sign::try_sign,
12+
util::{
13+
download, download_and_verify, extract_zip, HashAlgorithm, WEBVIEW2_BOOTSTRAPPER_URL,
14+
WEBVIEW2_X64_INSTALLER_GUID, WEBVIEW2_X86_INSTALLER_GUID, WIX_OUTPUT_FOLDER_NAME,
15+
WIX_UPDATER_OUTPUT_FOLDER_NAME,
16+
},
1417
},
1518
};
1619
use anyhow::{bail, Context};

tooling/bundler/src/bundle/windows/nsis.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// SPDX-License-Identifier: MIT
44

55
#[cfg(target_os = "windows")]
6-
use crate::bundle::windows::util::try_sign;
6+
use crate::bundle::windows::sign::{sign_command, try_sign};
77
use crate::{
88
bundle::{
99
common::CommandExt,
@@ -160,6 +160,7 @@ fn build_nsis_app_installer(
160160

161161
info!("Target: {}", arch);
162162

163+
// Code signing is currently only supported on Windows hosts
163164
#[cfg(target_os = "windows")]
164165
{
165166
let main_binary = settings
@@ -201,6 +202,18 @@ fn build_nsis_app_installer(
201202
data.insert("short_description", to_json(settings.short_description()));
202203
data.insert("copyright", to_json(settings.copyright_string()));
203204

205+
// Code signing is currently only supported on Windows hosts
206+
#[cfg(target_os = "windows")]
207+
if settings.can_sign() {
208+
data.insert(
209+
"uninstaller_sign_cmd",
210+
to_json(format!(
211+
"{:?}",
212+
sign_command("%1", &settings.sign_params())?.0
213+
)),
214+
);
215+
}
216+
204217
let version = settings.version_string();
205218
data.insert("version", to_json(version));
206219
data.insert(

tooling/bundler/src/bundle/windows/sign.rs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// SPDX-License-Identifier: Apache-2.0
44
// SPDX-License-Identifier: MIT
55

6-
use crate::bundle::common::CommandExt;
6+
use crate::{bundle::common::CommandExt, Settings};
77
use bitness::{self, Bitness};
88
use log::{debug, info};
99
use std::{
@@ -90,18 +90,11 @@ fn locate_signtool() -> crate::Result<PathBuf> {
9090
Err(crate::Error::SignToolNotFound)
9191
}
9292

93-
pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
94-
// Convert path to string reference, as we need to pass it as a command-line parameter to signtool
95-
let path_str = path.as_ref().to_str().unwrap();
96-
97-
info!(action = "Signing"; "{} with identity \"{}\"", path_str, params.certificate_thumbprint);
98-
93+
pub fn sign_command(path: &str, params: &SignParams) -> crate::Result<(Command, PathBuf)> {
9994
// Construct SignTool command
10095
let signtool = locate_signtool()?;
10196

102-
debug!("Running signtool {:?}", signtool);
103-
104-
let mut cmd = Command::new(signtool);
97+
let mut cmd = Command::new(&signtool);
10598
cmd.arg("sign");
10699
cmd.args(["/fd", &params.digest_algorithm]);
107100
cmd.args(["/sha1", &params.certificate_thumbprint]);
@@ -116,7 +109,18 @@ pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
116109
}
117110
}
118111

119-
cmd.arg(path_str);
112+
cmd.arg(path);
113+
114+
Ok((cmd, signtool))
115+
}
116+
117+
pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
118+
let path_str = path.as_ref().to_str().unwrap();
119+
120+
info!(action = "Signing"; "{} with identity \"{}\"", path_str, params.certificate_thumbprint);
121+
122+
let (mut cmd, signtool) = sign_command(path_str, params)?;
123+
debug!("Running signtool {:?}", signtool);
120124

121125
// Execute SignTool command
122126
let output = cmd.output_ok()?;
@@ -126,3 +130,39 @@ pub fn sign<P: AsRef<Path>>(path: P, params: &SignParams) -> crate::Result<()> {
126130

127131
Ok(())
128132
}
133+
134+
impl Settings {
135+
pub(crate) fn can_sign(&self) -> bool {
136+
self.windows().certificate_thumbprint.is_some()
137+
}
138+
pub(crate) fn sign_params(&self) -> SignParams {
139+
SignParams {
140+
product_name: self.product_name().into(),
141+
digest_algorithm: self
142+
.windows()
143+
.digest_algorithm
144+
.as_ref()
145+
.map(|algorithm| algorithm.to_string())
146+
.unwrap_or_else(|| "sha256".to_string()),
147+
certificate_thumbprint: self
148+
.windows()
149+
.certificate_thumbprint
150+
.clone()
151+
.unwrap_or_default(),
152+
timestamp_url: self
153+
.windows()
154+
.timestamp_url
155+
.as_ref()
156+
.map(|url| url.to_string()),
157+
tsp: self.windows().tsp,
158+
}
159+
}
160+
}
161+
162+
pub fn try_sign(file_path: &std::path::PathBuf, settings: &Settings) -> crate::Result<()> {
163+
if settings.can_sign() {
164+
info!(action = "Signing"; "{}", tauri_utils::display_path(file_path));
165+
sign(file_path, &settings.sign_params())?;
166+
}
167+
Ok(())
168+
}

tooling/bundler/src/bundle/windows/templates/installer.nsi

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ ${StrLoc}
3434
!define WEBVIEW2INSTALLERPATH "{{webview2_installer_path}}"
3535
!define UNINSTKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCTNAME}"
3636
!define MANUPRODUCTKEY "Software\${MANUFACTURER}\${PRODUCTNAME}"
37+
!define UNINSTALLERSIGNCOMMAND "{{uninstaller_sign_cmd}}"
3738

3839
Name "${PRODUCTNAME}"
3940
BrandingText "${COPYRIGHT}"
@@ -51,6 +52,10 @@ VIAddVersionKey "ProductVersion" "${VERSION}"
5152
!addplugindir "${PLUGINSPATH}"
5253
!endif
5354

55+
!if "${UNINSTALLERSIGNCOMMAND}" != ""
56+
!uninstfinalize '${UNINSTALLERSIGNCOMMAND}'
57+
!endif
58+
5459
; Handle install mode, `perUser`, `perMachine` or `both`
5560
!if "${INSTALLMODE}" == "perMachine"
5661
RequestExecutionLevel highest

tooling/bundler/src/bundle/windows/util.rs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,6 @@ use log::info;
1212
use sha2::Digest;
1313
use zip::ZipArchive;
1414

15-
#[cfg(target_os = "windows")]
16-
use crate::bundle::windows::sign::{sign, SignParams};
17-
#[cfg(target_os = "windows")]
18-
use crate::Settings;
19-
2015
pub const WEBVIEW2_BOOTSTRAPPER_URL: &str = "https://go.microsoft.com/fwlink/p/?LinkId=2124703";
2116
pub const WEBVIEW2_X86_INSTALLER_GUID: &str = "a17bde80-b5ab-47b5-8bbb-1cbe93fc6ec9";
2217
pub const WEBVIEW2_X64_INSTALLER_GUID: &str = "aa5fd9b3-dc11-4cbc-8343-a50f57b311e1";
@@ -75,35 +70,6 @@ fn verify(data: &Vec<u8>, hash: &str, mut hasher: impl Digest) -> crate::Result<
7570
}
7671
}
7772

78-
#[cfg(target_os = "windows")]
79-
pub fn try_sign(file_path: &std::path::PathBuf, settings: &Settings) -> crate::Result<()> {
80-
use tauri_utils::display_path;
81-
82-
if let Some(certificate_thumbprint) = settings.windows().certificate_thumbprint.as_ref() {
83-
info!(action = "Signing"; "{}", display_path(file_path));
84-
sign(
85-
file_path,
86-
&SignParams {
87-
product_name: settings.product_name().into(),
88-
digest_algorithm: settings
89-
.windows()
90-
.digest_algorithm
91-
.as_ref()
92-
.map(|algorithm| algorithm.to_string())
93-
.unwrap_or_else(|| "sha256".to_string()),
94-
certificate_thumbprint: certificate_thumbprint.to_string(),
95-
timestamp_url: settings
96-
.windows()
97-
.timestamp_url
98-
.as_ref()
99-
.map(|url| url.to_string()),
100-
tsp: settings.windows().tsp,
101-
},
102-
)?;
103-
}
104-
Ok(())
105-
}
106-
10773
/// Extracts the zips from memory into a useable path.
10874
pub fn extract_zip(data: &[u8], path: &Path) -> crate::Result<()> {
10975
let cursor = Cursor::new(data);

0 commit comments

Comments
 (0)