diff options
| author | David Lönnhager <david.l@mullvad.net> | 2025-04-02 13:49:15 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2025-04-03 14:10:58 +0200 |
| commit | 3d03dc779f6829a236d139543e3c19fa56c4a25e (patch) | |
| tree | 63656e7a74de1ee9ec416b23a80ec42c53dc304d | |
| parent | 00e26c1d17fb9044f8cbc0a168eab1edef70ba8a (diff) | |
| download | mullvadvpn-3d03dc779f6829a236d139543e3c19fa56c4a25e.tar.xz mullvadvpn-3d03dc779f6829a236d139543e3c19fa56c4a25e.zip | |
Set default pubkeys and pinned certificate in mullvad-update
| -rw-r--r-- | installer-downloader/src/controller.rs | 10 | ||||
| -rw-r--r-- | mullvad-api/src/version.rs | 3 | ||||
| -rw-r--r-- | mullvad-update/meta/src/platform.rs | 21 | ||||
| -rw-r--r-- | mullvad-update/src/client/api.rs | 12 | ||||
| -rw-r--r-- | mullvad-update/src/format/deserializer.rs | 15 | ||||
| -rw-r--r-- | mullvad-update/src/keys.rs | 6 | ||||
| -rw-r--r-- | mullvad-update/src/lib.rs | 3 |
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; |
