@@ -53,7 +53,11 @@ pub use self::{
5353use anyhow:: Context ;
5454pub use settings:: { NsisSettings , WindowsSettings , WixLanguage , WixLanguageConfig , WixSettings } ;
5555
56- use std:: { fmt:: Write , path:: PathBuf } ;
56+ use std:: {
57+ fmt:: Write ,
58+ io:: { Seek , SeekFrom } ,
59+ path:: PathBuf ,
60+ } ;
5761
5862/// Generated bundle metadata.
5963#[ derive( Debug ) ]
@@ -122,22 +126,46 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
122126 . iter ( )
123127 . find ( |b| b. main ( ) )
124128 . expect ( "Main binary missing in settings" ) ;
129+ let main_binary_path = settings. binary_path ( main_binary) ;
130+
131+ // When packaging multiple binary types, we make a copy of the unsigned main_binary so that we can
132+ // restore it after each package_type step. This avoids two issues:
133+ // - modifying a signed binary without updating its PE checksum can break signature verification
134+ // - codesigning tools should handle calculating+updating this, we just need to ensure
135+ // (re)signing is performed after every `patch_binary()` operation
136+ // - signing an already-signed binary can result in multiple signatures, causing verification errors
137+ let main_binary_reset_required =
138+ matches ! ( target_os, TargetPlatform :: Windows ) && settings. can_sign ( ) && package_types. len ( ) > 1 ;
139+ let mut unsigned_main_binary_copy = tempfile:: tempfile ( ) ?;
140+ if main_binary_reset_required {
141+ let mut unsigned_main_binary = std:: fs:: File :: open ( & main_binary_path) ?;
142+ std:: io:: copy ( & mut unsigned_main_binary, & mut unsigned_main_binary_copy) ?;
143+ }
125144
145+ let mut main_binary_signed = false ;
126146 let mut bundles = Vec :: < Bundle > :: new ( ) ;
127147 for package_type in & package_types {
128148 // bundle was already built! e.g. DMG already built .app
129149 if bundles. iter ( ) . any ( |b| b. package_type == * package_type) {
130150 continue ;
131151 }
132152
133- if let Err ( e) = patch_binary ( & settings . binary_path ( main_binary ) , package_type) {
153+ if let Err ( e) = patch_binary ( & main_binary_path , package_type) {
134154 log:: warn!( "Failed to add bundler type to the binary: {e}. Updater plugin may not be able to update this package. This shouldn't normally happen, please report it to https://github.com/tauri-apps/tauri/issues" ) ;
135155 }
136156
137157 // sign main binary for every package type after patch
138158 if matches ! ( target_os, TargetPlatform :: Windows ) && settings. can_sign ( ) {
139- let bin_path = settings. binary_path ( main_binary) ;
140- windows:: sign:: try_sign ( & bin_path, settings) ?;
159+ if main_binary_signed && main_binary_reset_required {
160+ let mut signed_main_binary = std:: fs:: OpenOptions :: new ( )
161+ . write ( true )
162+ . truncate ( true )
163+ . open ( & main_binary_path) ?;
164+ unsigned_main_binary_copy. seek ( SeekFrom :: Start ( 0 ) ) ?;
165+ std:: io:: copy ( & mut unsigned_main_binary_copy, & mut signed_main_binary) ?;
166+ }
167+ windows:: sign:: try_sign ( & main_binary_path, settings) ?;
168+ main_binary_signed = true ;
141169 }
142170
143171 let bundle_paths = match package_type {
@@ -160,6 +188,7 @@ pub fn bundle_project(settings: &Settings) -> crate::Result<Vec<Bundle>> {
160188
161189 #[ cfg( target_os = "windows" ) ]
162190 PackageType :: WindowsMsi => windows:: msi:: bundle_project ( settings, false ) ?,
191+ // note: don't restrict to windows as NSIS installers can be built in linux using cargo-xwin
163192 PackageType :: Nsis => windows:: nsis:: bundle_project ( settings, false ) ?,
164193
165194 #[ cfg( target_os = "linux" ) ]
0 commit comments