summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2025-04-02 13:49:15 +0200
committerDavid Lönnhager <david.l@mullvad.net>2025-04-03 14:10:58 +0200
commit3d03dc779f6829a236d139543e3c19fa56c4a25e (patch)
tree63656e7a74de1ee9ec416b23a80ec42c53dc304d
parent00e26c1d17fb9044f8cbc0a168eab1edef70ba8a (diff)
downloadmullvadvpn-3d03dc779f6829a236d139543e3c19fa56c4a25e.tar.xz
mullvadvpn-3d03dc779f6829a236d139543e3c19fa56c4a25e.zip
Set default pubkeys and pinned certificate in mullvad-update
-rw-r--r--installer-downloader/src/controller.rs10
-rw-r--r--mullvad-api/src/version.rs3
-rw-r--r--mullvad-update/meta/src/platform.rs21
-rw-r--r--mullvad-update/src/client/api.rs12
-rw-r--r--mullvad-update/src/format/deserializer.rs15
-rw-r--r--mullvad-update/src/keys.rs6
-rw-r--r--mullvad-update/src/lib.rs3
7 files changed, 40 insertions, 30 deletions
diff --git a/installer-downloader/src/controller.rs b/installer-downloader/src/controller.rs
index 2e2b44e351..f11d93d119 100644
--- a/installer-downloader/src/controller.rs
+++ b/installer-downloader/src/controller.rs
@@ -20,9 +20,6 @@ use tokio::{
task::JoinHandle,
};
-/// 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.mullvad.net/app/releases/";
@@ -49,12 +46,7 @@ pub fn initialize_controller<T: AppDelegate + 'static>(delegate: &mut T, environ
// Directory provider to use
type DirProvider = crate::temp::TempDirProvider;
- 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: mullvad_update::keys::TRUSTED_METADATA_SIGNING_PUBKEYS.clone(),
- };
+ let version_provider = HttpVersionInfoProvider::new(get_metadata_url());
AppController::initialize::<_, Downloader<T>, _, DirProvider>(
delegate,
diff --git a/mullvad-api/src/version.rs b/mullvad-api/src/version.rs
index 0fd070910b..ef68525802 100644
--- a/mullvad-api/src/version.rs
+++ b/mullvad-api/src/version.rs
@@ -66,8 +66,7 @@ impl AppVersionProxy {
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_with_keys(
- &mullvad_update::keys::TRUSTED_METADATA_SIGNING_PUBKEYS,
+ let response = mullvad_update::format::SignedResponse::deserialize_and_verify(
&bytes,
lowest_metadata_version,
)
diff --git a/mullvad-update/meta/src/platform.rs b/mullvad-update/meta/src/platform.rs
index 9d47825bc2..ed08915092 100644
--- a/mullvad-update/meta/src/platform.rs
+++ b/mullvad-update/meta/src/platform.rs
@@ -10,7 +10,6 @@ use std::{
fmt,
path::{Path, PathBuf},
str::FromStr,
- sync::LazyLock,
};
use tokio::{fs, io};
@@ -23,12 +22,6 @@ use crate::{
/// Actual JSON files should be stored at `<base url>/<platform>.json`.
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(|| {
- const CERT_BYTES: &[u8] = include_bytes!("../../../mullvad-api/le_root_cert.pem");
- reqwest::Certificate::from_pem(CERT_BYTES).expect("invalid cert")
-});
-
#[derive(Clone, Copy)]
pub enum Platform {
Windows,
@@ -127,11 +120,7 @@ impl Platform {
println!("Pulling {self} metadata from {url}...");
- let version_provider = HttpVersionInfoProvider {
- pinned_certificate: Some(PINNED_CERTIFICATE.clone()),
- url,
- verifying_keys: mullvad_update::keys::TRUSTED_METADATA_SIGNING_PUBKEYS.clone(),
- };
+ let version_provider = HttpVersionInfoProvider::new(url);
let response = version_provider
.get_versions(crate::MIN_VERIFY_METADATA_VERSION)
.await
@@ -231,12 +220,8 @@ impl Platform {
println!("Verifying signature of {}...", signed_path.display());
let bytes = fs::read(signed_path).await.context("Failed to read file")?;
- format::SignedResponse::deserialize_and_verify_with_keys(
- &mullvad_update::keys::TRUSTED_METADATA_SIGNING_PUBKEYS,
- &bytes,
- crate::MIN_VERIFY_METADATA_VERSION,
- )
- .context("Failed to verify metadata for {platform}: {error}")?;
+ format::SignedResponse::deserialize_and_verify(&bytes, crate::MIN_VERIFY_METADATA_VERSION)
+ .context("Failed to verify metadata for {platform}: {error}")?;
Ok(())
}
diff --git a/mullvad-update/src/client/api.rs b/mullvad-update/src/client/api.rs
index 7811dee275..05c8359449 100644
--- a/mullvad-update/src/client/api.rs
+++ b/mullvad-update/src/client/api.rs
@@ -35,6 +35,18 @@ impl HttpVersionInfoProvider {
/// Maximum size of the GET response, in bytes
const SIZE_LIMIT: usize = 1024 * 1024;
+ /// Construct an [HttpVersionInfoProvider] for `url` using reasonable defaults.
+ ///
+ /// By default, `pinned_certificate` will be set to the LE root certificate, and
+ /// `verifying_keys` will be set to the keys in `trusted-metadata-signing-keys`.
+ pub fn new(url: String) -> Self {
+ HttpVersionInfoProvider {
+ url,
+ pinned_certificate: Some(crate::keys::PINNED_CERTIFICATE.clone()),
+ verifying_keys: crate::keys::TRUSTED_METADATA_SIGNING_PUBKEYS.clone(),
+ }
+ }
+
/// Download and verify signed data
pub async fn get_versions(
&self,
diff --git a/mullvad-update/src/format/deserializer.rs b/mullvad-update/src/format/deserializer.rs
index 980fd52ca6..8b138578b5 100644
--- a/mullvad-update/src/format/deserializer.rs
+++ b/mullvad-update/src/format/deserializer.rs
@@ -10,6 +10,21 @@ use super::{PartialSignedResponse, ResponseSignature, SignedResponse};
impl SignedResponse {
/// Deserialize some bytes to JSON, and verify them, including signature and expiry.
/// If successful, the deserialized data is returned.
+ ///
+ /// This uses the keys in `trusted-metadata-signing-pubkeys`
+ pub fn deserialize_and_verify(
+ bytes: &[u8],
+ min_metadata_version: usize,
+ ) -> Result<Self, anyhow::Error> {
+ Self::deserialize_and_verify_with_keys(
+ &crate::keys::TRUSTED_METADATA_SIGNING_PUBKEYS,
+ bytes,
+ min_metadata_version,
+ )
+ }
+
+ /// Deserialize some bytes to JSON, and verify them, including signature and expiry.
+ /// If successful, the deserialized data is returned.
pub fn deserialize_and_verify_with_keys(
keys: &Vec1<VerifyingKey>,
bytes: &[u8],
diff --git a/mullvad-update/src/keys.rs b/mullvad-update/src/keys.rs
index 9b0abf5c6b..73ca8519d0 100644
--- a/mullvad-update/src/keys.rs
+++ b/mullvad-update/src/keys.rs
@@ -4,6 +4,12 @@ use crate::format::key::VerifyingKey;
use std::sync::LazyLock;
use vec1::Vec1;
+/// Default TLS certificate to pin to
+pub static PINNED_CERTIFICATE: LazyLock<reqwest::Certificate> = LazyLock::new(|| {
+ const CERT_BYTES: &[u8] = include_bytes!("../../mullvad-api/le_root_cert.pem");
+ reqwest::Certificate::from_pem(CERT_BYTES).expect("invalid cert")
+});
+
/// 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")));
diff --git a/mullvad-update/src/lib.rs b/mullvad-update/src/lib.rs
index 88e78034a7..eb9877f62f 100644
--- a/mullvad-update/src/lib.rs
+++ b/mullvad-update/src/lib.rs
@@ -6,7 +6,8 @@ mod client;
#[cfg(feature = "client")]
pub use client::*;
-pub mod keys;
+#[cfg(feature = "client")]
+mod keys;
pub mod version;