Skip to content

Commit d95cc83

Browse files
committed
feat: enforce updater public key [TRI-015] (#42)
1 parent b43019a commit d95cc83

8 files changed

Lines changed: 40 additions & 42 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch
3+
"tauri-utils": patch
4+
---
5+
6+
The updater `pubkey` is now a required field for security reasons. Sign your updates with the `tauri signer` command.

core/tauri-utils/src/config.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1373,8 +1373,8 @@ pub struct UpdaterConfig {
13731373
pub dialog: bool,
13741374
/// The updater endpoints.
13751375
pub endpoints: Option<Vec<String>>,
1376-
/// Optional pubkey.
1377-
pub pubkey: Option<String>,
1376+
/// Signature public key.
1377+
pub pubkey: String,
13781378
}
13791379

13801380
impl Default for UpdaterConfig {
@@ -1383,7 +1383,7 @@ impl Default for UpdaterConfig {
13831383
active: false,
13841384
dialog: default_dialog(),
13851385
endpoints: None,
1386-
pubkey: None,
1386+
pubkey: "".into(),
13871387
}
13881388
}
13891389
}
@@ -2028,7 +2028,7 @@ mod build {
20282028
fn to_tokens(&self, tokens: &mut TokenStream) {
20292029
let active = self.active;
20302030
let dialog = self.dialog;
2031-
let pubkey = opt_str_lit(self.pubkey.as_ref());
2031+
let pubkey = str_lit(&self.pubkey);
20322032
let endpoints = opt_vec_str_lit(self.endpoints.as_ref());
20332033

20342034
literal_struct!(tokens, UpdaterConfig, active, dialog, pubkey, endpoints);
@@ -2234,7 +2234,7 @@ mod test {
22342234
updater: UpdaterConfig {
22352235
active: false,
22362236
dialog: true,
2237-
pubkey: None,
2237+
pubkey: "".into(),
22382238
endpoints: None,
22392239
},
22402240
security: SecurityConfig {

core/tauri/src/updater/core.rs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ use crate::api::{
1111
use base64::decode;
1212
use http::StatusCode;
1313
use minisign_verify::{PublicKey, Signature};
14-
use tauri_utils::Env;
1514
use tauri_utils::platform::current_exe;
15+
use tauri_utils::Env;
1616

1717
use std::{
1818
collections::HashMap,
@@ -405,7 +405,7 @@ pub struct Update {
405405
impl Update {
406406
// Download and install our update
407407
// @todo(lemarier): Split into download and install (two step) but need to be thread safe
408-
pub async fn download_and_install(&self, pub_key: Option<String>) -> Result {
408+
pub async fn download_and_install(&self, pub_key: String) -> Result {
409409
// download url for selected release
410410
let url = self.download_url.as_str();
411411
// extract path
@@ -453,18 +453,15 @@ impl Update {
453453
// create memory buffer from our archive (Seek + Read)
454454
let mut archive_buffer = Cursor::new(resp.data);
455455

456-
// Validate signature ONLY if pubkey is available in tauri.conf.json
457-
if let Some(pub_key) = pub_key {
458-
// We need an announced signature by the server
459-
// if there is no signature, bail out.
460-
if let Some(signature) = &self.signature {
461-
// we make sure the archive is valid and signed with the private key linked with the publickey
462-
verify_signature(&mut archive_buffer, signature, &pub_key)?;
463-
} else {
464-
// We have a public key inside our source file, but not announced by the server,
465-
// we assume this update is NOT valid.
466-
return Err(Error::PubkeyButNoSignature);
467-
}
456+
// We need an announced signature by the server
457+
// if there is no signature, bail out.
458+
if let Some(signature) = &self.signature {
459+
// we make sure the archive is valid and signed with the private key linked with the publickey
460+
verify_signature(&mut archive_buffer, signature, &pub_key)?;
461+
} else {
462+
// We have a public key inside our source file, but not announced by the server,
463+
// we assume this update is NOT valid.
464+
return Err(Error::MissingUpdaterSignature);
468465
}
469466

470467
// we copy the files depending of the operating system
@@ -1174,7 +1171,7 @@ mod test {
11741171
assert_eq!(updater.version, "2.0.1");
11751172

11761173
// download, install and validate signature
1177-
let install_process = block!(updater.download_and_install(Some(pubkey)));
1174+
let install_process = block!(updater.download_and_install(pubkey));
11781175
assert!(install_process.is_ok());
11791176

11801177
// make sure the extraction went well (it should have skipped the main app.app folder)

core/tauri/src/updater/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ pub enum Error {
4545
#[error("Unsuported operating system or platform")]
4646
UnsupportedPlatform,
4747
/// Public key found in `tauri.conf.json` but no signature announced remotely.
48-
#[error("Signature not available but public key provided, skipping update")]
49-
PubkeyButNoSignature,
48+
#[error("Signature not available, skipping update")]
49+
MissingUpdaterSignature,
5050
/// Triggered when there is NO error and the two versions are equals.
5151
/// On client side, it's important to catch this error.
5252
#[error("No updates available")]

core/tauri/src/updater/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -528,7 +528,7 @@ async fn prompt_for_install<R: Runtime>(
528528
updater: &self::core::Update,
529529
app_name: &str,
530530
body: &str,
531-
pubkey: Option<String>,
531+
pubkey: String,
532532
) -> crate::Result<()> {
533533
// remove single & double quote
534534
let escaped_body = body.replace(&['\"', '\''][..], "");

tooling/bundler/src/bundle/settings.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ pub struct UpdaterSettings {
113113
pub active: bool,
114114
/// The updater endpoints.
115115
pub endpoints: Option<Vec<String>>,
116-
/// Optional pubkey.
117-
pub pubkey: Option<String>,
116+
/// Signature public key.
117+
pub pubkey: String,
118118
/// Display built-in dialog or use event system if disabled.
119119
pub dialog: bool,
120120
}
@@ -680,14 +680,6 @@ impl Settings {
680680
}
681681
}
682682

683-
/// Is pubkey provided?
684-
pub fn is_updater_pubkey(&self) -> bool {
685-
match &self.bundle_settings.updater {
686-
Some(val) => val.pubkey.is_some(),
687-
None => false,
688-
}
689-
}
690-
691683
/// Get pubkey (mainly for testing)
692684
#[cfg(test)]
693685
pub fn updater_pubkey(&self) -> Option<&str> {

tooling/cli.rs/schema.json

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@
159159
"security": {},
160160
"updater": {
161161
"active": false,
162-
"dialog": true
162+
"dialog": true,
163+
"pubkey": ""
163164
},
164165
"windows": [
165166
{
@@ -1493,7 +1494,8 @@
14931494
"description": "The updater configuration.",
14941495
"default": {
14951496
"active": false,
1496-
"dialog": true
1497+
"dialog": true,
1498+
"pubkey": ""
14971499
},
14981500
"allOf": [
14991501
{
@@ -1534,6 +1536,9 @@
15341536
"UpdaterConfig": {
15351537
"description": "The Updater configuration object.",
15361538
"type": "object",
1539+
"required": [
1540+
"pubkey"
1541+
],
15371542
"properties": {
15381543
"active": {
15391544
"description": "Whether the updater is active or not.",
@@ -1556,11 +1561,8 @@
15561561
}
15571562
},
15581563
"pubkey": {
1559-
"description": "Optional pubkey.",
1560-
"type": [
1561-
"string",
1562-
"null"
1563-
]
1564+
"description": "Signature public key.",
1565+
"type": "string"
15641566
}
15651567
},
15661568
"additionalProperties": false

tooling/cli.rs/src/build.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,8 @@ pub fn command(options: Options) -> Result<()> {
270270

271271
let bundles = bundle_project(settings).with_context(|| "failed to bundle project")?;
272272

273-
// If updater is active and pubkey is available
274-
if config_.tauri.updater.active && config_.tauri.updater.pubkey.is_some() {
273+
// If updater is active
274+
if config_.tauri.updater.active {
275275
// make sure we have our package builts
276276
let mut signed_paths = Vec::new();
277277
for elem in bundles
@@ -286,6 +286,7 @@ pub fn command(options: Options) -> Result<()> {
286286
signed_paths.append(&mut vec![signature_path]);
287287
}
288288
}
289+
289290
if !signed_paths.is_empty() {
290291
print_signed_updater_archive(&signed_paths)?;
291292
}

0 commit comments

Comments
 (0)