summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2025-03-26 09:40:41 +0100
committerDavid Lönnhager <david.l@mullvad.net>2025-03-28 17:04:58 +0100
commit200d4837f16cc9501bdeefc034d16646f56f303d (patch)
treef38192648b661d914c2f152bb5a81628181a4a2e
parent8fbf46194b975655f6b0e5131032d17ed47875fc (diff)
downloadmullvadvpn-200d4837f16cc9501bdeefc034d16646f56f303d.tar.xz
mullvadvpn-200d4837f16cc9501bdeefc034d16646f56f303d.zip
Switch from staging to prod for version metadata
-rw-r--r--.github/CODEOWNERS1
-rw-r--r--Cargo.lock3
-rw-r--r--installer-downloader/Cargo.toml1
-rw-r--r--installer-downloader/src/controller.rs12
-rw-r--r--mullvad-api/Cargo.toml1
-rw-r--r--mullvad-api/src/version.rs11
-rw-r--r--mullvad-update/meta/Cargo.toml1
-rw-r--r--mullvad-update/meta/src/main.rs3
-rw-r--r--mullvad-update/meta/src/platform.rs14
-rw-r--r--mullvad-update/src/format/key.rs3
-rw-r--r--mullvad-update/src/keys.rs41
-rw-r--r--mullvad-update/src/lib.rs2
-rw-r--r--mullvad-update/stagemole-pubkey1
-rw-r--r--mullvad-update/trusted-metadata-signing-pubkeys8
-rw-r--r--test/Cargo.lock1
15 files changed, 59 insertions, 44 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 44c9aac731..27fa5531dc 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -8,6 +8,7 @@
# Developer signing keys must be approved by team/tech leads
/ci/keys/ @faern @raksooo @pinkisemils @albin-mullvad
+/mullvad-update/trusted-metadata-signing-pubkeys @faern @raksooo @pinkisemils @albin-mullvad
# Desktop build server files owned by desktop leads
/ci/buildserver* @faern @raksooo
diff --git a/Cargo.lock b/Cargo.lock
index 37ad7063b9..43d8a8624f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2041,7 +2041,6 @@ dependencies = [
"serde",
"talpid-platform-metadata",
"tokio",
- "vec1",
"windows-sys 0.52.0",
"winres",
]
@@ -2496,7 +2495,6 @@ dependencies = [
"sha2",
"tokio",
"toml 0.8.19",
- "vec1",
]
[[package]]
@@ -2638,7 +2636,6 @@ dependencies = [
"tokio-socks",
"tower 0.5.1",
"uuid",
- "vec1",
]
[[package]]
diff --git a/installer-downloader/Cargo.toml b/installer-downloader/Cargo.toml
index 98a6182b86..c24011d126 100644
--- a/installer-downloader/Cargo.toml
+++ b/installer-downloader/Cargo.toml
@@ -28,7 +28,6 @@ rand = { version = "0.8.5" }
reqwest = { version = "0.12.9", default-features = false, features = ["rustls-tls"] }
serde = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["rt-multi-thread", "fs"] }
-vec1 = { workspace = true }
talpid-platform-metadata = { path = "../talpid-platform-metadata" }
mullvad-update = { path = "../mullvad-update", features = ["client"] }
diff --git a/installer-downloader/src/controller.rs b/installer-downloader/src/controller.rs
index fd9c931756..2e2b44e351 100644
--- a/installer-downloader/src/controller.rs
+++ b/installer-downloader/src/controller.rs
@@ -19,17 +19,13 @@ use tokio::{
sync::{mpsc, oneshot},
task::JoinHandle,
};
-use vec1::vec1;
-
-/// ed25519 pubkey used to verify metadata from the Mullvad (stagemole) API
-const VERSION_PROVIDER_PUBKEY: &str = include_str!("../../mullvad-update/stagemole-pubkey");
/// Pinned root certificate used when fetching version metadata
const PINNED_CERTIFICATE: &[u8] = include_bytes!("../../mullvad-api/le_root_cert.pem");
/// Base URL for pulling metadata. Actual JSON files should be stored at `<base
/// url>/<platform>.json`
-const META_REPOSITORY_URL: &str = "https://api.stagemole.eu/app/releases/";
+const META_REPOSITORY_URL: &str = "https://api.mullvad.net/app/releases/";
/// Actions handled by an async worker task in [ActionMessageHandler].
enum TaskMessage {
@@ -53,15 +49,11 @@ pub fn initialize_controller<T: AppDelegate + 'static>(delegate: &mut T, environ
// Directory provider to use
type DirProvider = crate::temp::TempDirProvider;
- // Version info provider to use
- let verifying_key =
- mullvad_update::format::key::VerifyingKey::from_hex(VERSION_PROVIDER_PUBKEY)
- .expect("valid key");
let cert = reqwest::Certificate::from_pem(PINNED_CERTIFICATE).expect("invalid cert");
let version_provider = HttpVersionInfoProvider {
url: get_metadata_url(),
pinned_certificate: Some(cert),
- verifying_keys: vec1![verifying_key],
+ verifying_keys: mullvad_update::keys::TRUSTED_METADATA_SIGNING_PUBKEYS.clone(),
};
AppController::initialize::<_, Downloader<T>, _, DirProvider>(
diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml
index f55ab02f9a..57ace41064 100644
--- a/mullvad-api/Cargo.toml
+++ b/mullvad-api/Cargo.toml
@@ -47,7 +47,6 @@ tokio-rustls = { version = "0.26.0", features = [
tokio-socks = "0.5.1"
rustls-pemfile = "2.1.3"
uuid = { version = "1.4.1", features = ["v4"] }
-vec1 = { workspace = true }
mullvad-encrypted-dns-proxy = { path = "../mullvad-encrypted-dns-proxy" }
mullvad-fs = { path = "../mullvad-fs" }
diff --git a/mullvad-api/src/version.rs b/mullvad-api/src/version.rs
index 26cbe1d2d3..8218b94701 100644
--- a/mullvad-api/src/version.rs
+++ b/mullvad-api/src/version.rs
@@ -4,7 +4,6 @@ use std::sync::Arc;
use http::StatusCode;
use mullvad_types::version::AppVersion;
use mullvad_update::version::{VersionInfo, VersionParameters};
-use vec1::vec1;
use super::rest;
use super::APP_URL_PREFIX;
@@ -23,9 +22,6 @@ pub struct AppVersionResponse {
}
impl AppVersionProxy {
- /// Public key to use for `version_check_2` response
- const VERSION_PROVIDER_PUBKEY: &str = include_str!("../../mullvad-update/stagemole-pubkey");
-
/// Maximum size of `version_check_2` response
const SIZE_LIMIT: usize = 1024 * 1024;
@@ -65,18 +61,13 @@ impl AppVersionProxy {
let path = format!("app/releases/{platform}.json");
let request = self.handle.factory.get(&path);
- let verifying_key =
- mullvad_update::format::key::VerifyingKey::from_hex(Self::VERSION_PROVIDER_PUBKEY)
- .expect("valid key");
- let verifying_keys = vec1![verifying_key];
-
async move {
let request = request?.expected_status(&[StatusCode::OK]);
let response = service.request(request).await?;
let bytes = response.body_with_max_size(Self::SIZE_LIMIT).await?;
let response = mullvad_update::format::SignedResponse::deserialize_and_verify(
- &verifying_keys,
+ &mullvad_update::keys::TRUSTED_METADATA_SIGNING_PUBKEYS,
&bytes,
lowest_metadata_version,
)
diff --git a/mullvad-update/meta/Cargo.toml b/mullvad-update/meta/Cargo.toml
index 932b5780b2..7eba726e95 100644
--- a/mullvad-update/meta/Cargo.toml
+++ b/mullvad-update/meta/Cargo.toml
@@ -22,7 +22,6 @@ serde = { workspace = true }
sha2 = "0.10"
tokio = { version = "1", features = ["full"] }
toml = "0.8"
-vec1 = { workspace = true }
mullvad-version = { path = "../../mullvad-version", features = ["serde"] }
mullvad-update = { path = "../", features = ["client", "sign"] }
diff --git a/mullvad-update/meta/src/main.rs b/mullvad-update/meta/src/main.rs
index 51d2c5ba25..478213b82c 100644
--- a/mullvad-update/meta/src/main.rs
+++ b/mullvad-update/meta/src/main.rs
@@ -28,9 +28,6 @@ const DEFAULT_ROLLOUT: f32 = 1.;
/// Lowest version to accept using 'verify'
const MIN_VERIFY_METADATA_VERSION: usize = 0;
-/// Verification public key
-const VERIFYING_PUBKEY: &str = include_str!("../../stagemole-pubkey");
-
/// A tool that generates signed Mullvad version metadata.
///
/// Unsigned work is stored in `work/`, and signed work is stored in `signed/`
diff --git a/mullvad-update/meta/src/platform.rs b/mullvad-update/meta/src/platform.rs
index 00a391a925..e1f471f2e5 100644
--- a/mullvad-update/meta/src/platform.rs
+++ b/mullvad-update/meta/src/platform.rs
@@ -13,7 +13,6 @@ use std::{
sync::LazyLock,
};
use tokio::{fs, io};
-use vec1::vec1;
use crate::{
artifacts,
@@ -22,7 +21,7 @@ use crate::{
/// Base URL for metadata found with `meta pull`.
/// Actual JSON files should be stored at `<base url>/<platform>.json`.
-const META_REPOSITORY_URL: &str = "https://releases.stagemole.eu/desktop/metadata/";
+const META_REPOSITORY_URL: &str = "https://releases.mullvad.net/desktop/metadata/";
/// TLS certificate to pin to for `meta pull`.
static PINNED_CERTIFICATE: LazyLock<reqwest::Certificate> = LazyLock::new(|| {
@@ -128,14 +127,10 @@ impl Platform {
println!("Pulling {self} metadata from {url}...");
- // Pull latest metadata
- let verifying_key =
- key::VerifyingKey::from_hex(crate::VERIFYING_PUBKEY).expect("Invalid pubkey");
-
let version_provider = HttpVersionInfoProvider {
pinned_certificate: Some(PINNED_CERTIFICATE.clone()),
url,
- verifying_keys: vec1![verifying_key],
+ verifying_keys: mullvad_update::keys::TRUSTED_METADATA_SIGNING_PUBKEYS.clone(),
};
let response = version_provider
.get_versions(crate::MIN_VERIFY_METADATA_VERSION)
@@ -236,11 +231,8 @@ impl Platform {
println!("Verifying signature of {}...", signed_path.display());
let bytes = fs::read(signed_path).await.context("Failed to read file")?;
- let public_key = key::VerifyingKey::from_hex(include_str!("../../stagemole-pubkey"))
- .expect("Invalid pubkey");
-
format::SignedResponse::deserialize_and_verify(
- &vec1![public_key],
+ &mullvad_update::keys::TRUSTED_METADATA_SIGNING_PUBKEYS,
&bytes,
crate::MIN_VERIFY_METADATA_VERSION,
)
diff --git a/mullvad-update/src/format/key.rs b/mullvad-update/src/format/key.rs
index c61b2dd9e0..1f49efa5c4 100644
--- a/mullvad-update/src/format/key.rs
+++ b/mullvad-update/src/format/key.rs
@@ -75,8 +75,7 @@ impl Serialize for SecretKey {
}
/// ed25519 verifying key
-#[derive(Debug, PartialEq, Eq)]
-#[cfg_attr(test, derive(Clone))]
+#[derive(Debug, PartialEq, Eq, Clone)]
pub struct VerifyingKey(pub ed25519_dalek::VerifyingKey);
impl fmt::Display for VerifyingKey {
diff --git a/mullvad-update/src/keys.rs b/mullvad-update/src/keys.rs
new file mode 100644
index 0000000000..9b0abf5c6b
--- /dev/null
+++ b/mullvad-update/src/keys.rs
@@ -0,0 +1,41 @@
+//! Keys that may be used for verifying data
+
+use crate::format::key::VerifyingKey;
+use std::sync::LazyLock;
+use vec1::Vec1;
+
+/// Pubkeys used to verify metadata from the Mullvad API (production)
+pub static TRUSTED_METADATA_SIGNING_PUBKEYS: LazyLock<Vec1<VerifyingKey>> =
+ LazyLock::new(|| parse_keys(include_str!("../trusted-metadata-signing-pubkeys")));
+
+fn parse_keys(keys: &str) -> Vec1<VerifyingKey> {
+ let mut v = vec![];
+ for key in keys.split('\n') {
+ let key = key.trim();
+ if key.starts_with('#') || key.is_empty() {
+ continue;
+ }
+ v.push(VerifyingKey::from_hex(key).expect("invalid pubkey"));
+ }
+ v.try_into().expect("need at least one key")
+}
+
+#[cfg(test)]
+#[test]
+fn test_parse_keys() {
+ let key1 = "AB4EF63FFDCC6BD5A19C30CD23B9DE03099407A04463418F17AE338B98AA09D4".to_lowercase();
+ let key2 = "BB4EF63FFDCC6BD5A19C30CD23B9DE03099407A04463418F17AE338B98AA09D4".to_lowercase();
+ let keys = parse_keys(&format!(
+ r#"
+# test
+{key1}
+# test 2
+{key2}
+"#
+ ));
+ assert_eq!(format!("{}", keys[0]), key1);
+ assert_eq!(format!("{}", keys[1]), key2);
+
+ // Test that actual keys are validly parsed
+ let _prod = &*TRUSTED_METADATA_SIGNING_PUBKEYS;
+}
diff --git a/mullvad-update/src/lib.rs b/mullvad-update/src/lib.rs
index 4efd060b4b..88e78034a7 100644
--- a/mullvad-update/src/lib.rs
+++ b/mullvad-update/src/lib.rs
@@ -6,6 +6,8 @@ mod client;
#[cfg(feature = "client")]
pub use client::*;
+pub mod keys;
+
pub mod version;
/// Parser and serializer for version metadata
diff --git a/mullvad-update/stagemole-pubkey b/mullvad-update/stagemole-pubkey
deleted file mode 100644
index 256a77bafc..0000000000
--- a/mullvad-update/stagemole-pubkey
+++ /dev/null
@@ -1 +0,0 @@
-a0cd8f582e3147d57f7c01ec0fd306c8315290cea55725c7d5c76f835b78b363 \ No newline at end of file
diff --git a/mullvad-update/trusted-metadata-signing-pubkeys b/mullvad-update/trusted-metadata-signing-pubkeys
new file mode 100644
index 0000000000..f5d427f980
--- /dev/null
+++ b/mullvad-update/trusted-metadata-signing-pubkeys
@@ -0,0 +1,8 @@
+# linus
+c99b5e6e76bb7ab5b6fc3cdfe146faaa8afcfce0326822fe1629e00e666988b4
+# oskar
+4f2faeeda078df4dc1eebfb217c475eec9635c65d8b075171c4cf1808356dde5
+# emils
+299e8b06355031781623de7c9a013eb88b07aee80476e2938ce01a7b623d29d0
+# albin
+af4f7762e3e13af87f2e8c8af8ce5cce456cd3390af6d881b1eb6f802786cc3f
diff --git a/test/Cargo.lock b/test/Cargo.lock
index 3cde6c40bd..80c061ad0e 100644
--- a/test/Cargo.lock
+++ b/test/Cargo.lock
@@ -2101,7 +2101,6 @@ dependencies = [
"tokio-socks",
"tower 0.5.1",
"uuid",
- "vec1",
]
[[package]]