summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock3
-rw-r--r--mullvad-api/Cargo.toml3
-rw-r--r--mullvad-api/src/lib.rs48
-rw-r--r--mullvad-api/src/rest.rs26
-rw-r--r--test/Cargo.lock79
5 files changed, 155 insertions, 4 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e9d7219758..37ad7063b9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2609,6 +2609,7 @@ dependencies = [
name = "mullvad-api"
version = "0.0.0"
dependencies = [
+ "anyhow",
"async-trait",
"cbindgen 0.28.0",
"chrono",
@@ -2624,6 +2625,7 @@ dependencies = [
"mullvad-encrypted-dns-proxy",
"mullvad-fs",
"mullvad-types",
+ "mullvad-update",
"rustls-pemfile 2.1.3",
"serde",
"serde_json",
@@ -2636,6 +2638,7 @@ dependencies = [
"tokio-socks",
"tower 0.5.1",
"uuid",
+ "vec1",
]
[[package]]
diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml
index 46eb8fbaff..72c8370e65 100644
--- a/mullvad-api/Cargo.toml
+++ b/mullvad-api/Cargo.toml
@@ -15,6 +15,7 @@ workspace = true
api-override = []
[dependencies]
+anyhow = { workspace = true }
async-trait = "0.1"
libc = "0.2"
chrono = { workspace = true }
@@ -46,10 +47,12 @@ 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" }
mullvad-types = { path = "../mullvad-types" }
+mullvad-update = { path = "../mullvad-update", features = ["client"] }
talpid-types = { path = "../talpid-types" }
talpid-time = { path = "../talpid-time" }
diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs
index d8980a17b5..86f83aa842 100644
--- a/mullvad-api/src/lib.rs
+++ b/mullvad-api/src/lib.rs
@@ -9,6 +9,7 @@ use mullvad_types::{
account::{AccountData, AccountNumber, VoucherSubmission},
version::AppVersion,
};
+use mullvad_update::version::{VersionInfo, VersionParameters};
use proxy::{ApiConnectionMode, ConnectionModeProvider};
use std::{
collections::BTreeMap,
@@ -19,6 +20,7 @@ use std::{
sync::Arc,
};
use talpid_types::ErrorExt;
+use vec1::vec1;
pub mod availability;
use availability::ApiAvailability;
@@ -712,6 +714,8 @@ pub struct AppVersionResponse {
}
impl AppVersionProxy {
+ const VERSION_PROVIDER_PUBKEY: &str = include_str!("../../mullvad-update/stagemole-pubkey");
+
pub fn new(handle: rest::MullvadRestHandle) -> Self {
Self { handle }
}
@@ -735,6 +739,50 @@ impl AppVersionProxy {
response.deserialize().await
}
}
+
+ /// Get versions from `/app/releases/<platform>.json`
+ pub fn version_check_2(
+ &self,
+ platform: &str,
+ architecture: mullvad_update::format::Architecture,
+ rollout: f32,
+ lowest_metadata_version: usize,
+ ) -> impl Future<Output = Result<VersionInfo, rest::Error>> + use<> {
+ // Maximum size of version response
+ const SIZE_LIMIT: usize = 1024 * 1024;
+
+ let service = self.handle.service.clone();
+ 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(SIZE_LIMIT).await?;
+
+ let response = mullvad_update::format::SignedResponse::deserialize_and_verify(
+ &verifying_keys,
+ &bytes,
+ lowest_metadata_version,
+ )
+ .map_err(|err| rest::Error::FetchVersions(Arc::new(err)))?;
+
+ let params = VersionParameters {
+ architecture,
+ rollout,
+ lowest_metadata_version,
+ };
+
+ VersionInfo::try_from_response(&params, response.signed)
+ .map_err(Arc::new)
+ .map_err(rest::Error::FetchVersions)
+ }
+ }
}
#[derive(Clone)]
diff --git a/mullvad-api/src/rest.rs b/mullvad-api/src/rest.rs
index bab6d8112a..94c43392ad 100644
--- a/mullvad-api/src/rest.rs
+++ b/mullvad-api/src/rest.rs
@@ -13,7 +13,7 @@ use futures::{
};
use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
use hyper::{
- body::{Body, Bytes, Incoming},
+ body::{Body, Buf, Bytes, Incoming},
header::{self, HeaderValue},
Method, Uri,
};
@@ -73,6 +73,14 @@ pub enum Error {
#[error("Set account number on factory with no access token store")]
NoAccessTokenStore,
+
+ /// Failed to obtain versions
+ #[error("Failed to obtain versions")]
+ FetchVersions(#[from] Arc<anyhow::Error>),
+
+ /// Body exceeded size limit
+ #[error("Body exceeded size limit")]
+ BodyTooLarge,
}
impl From<Infallible> for Error {
@@ -493,7 +501,7 @@ pub struct Response<B> {
response: hyper::Response<B>,
}
-impl<B: Body> Response<B>
+impl<B: Body + Unpin> Response<B>
where
Error: From<<B as Body>::Error>,
{
@@ -516,6 +524,20 @@ where
pub async fn body(self) -> Result<Vec<u8>> {
Ok(BodyExt::collect(self.response).await?.to_bytes().to_vec())
}
+
+ pub async fn body_with_max_size(self, size_limit: usize) -> Result<Vec<u8>> {
+ let mut data: Vec<u8> = vec![];
+ let mut stream = self.response.into_data_stream();
+
+ while let Some(chunk) = stream.next().await {
+ data.extend(chunk?.chunk());
+ if data.len() > size_limit {
+ return Err(Error::BodyTooLarge);
+ }
+ }
+
+ Ok(data)
+ }
}
#[derive(serde::Deserialize)]
diff --git a/test/Cargo.lock b/test/Cargo.lock
index e2e014cce3..3cde6c40bd 100644
--- a/test/Cargo.lock
+++ b/test/Cargo.lock
@@ -642,6 +642,7 @@ dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
+ "digest",
"fiat-crypto",
"rustc_version",
"subtle",
@@ -769,6 +770,21 @@ dependencies = [
]
[[package]]
+name = "ed25519-dalek"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871"
+dependencies = [
+ "curve25519-dalek",
+ "ed25519",
+ "rand_core 0.6.4",
+ "serde",
+ "sha2",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
name = "educe"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1800,6 +1816,17 @@ dependencies = [
]
[[package]]
+name = "json-canon"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "447ae153a2bd47d61acc0d131295408e32ef87ed9785825a6f4ecef85afc0edb"
+dependencies = [
+ "ryu-js",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
name = "kqueue"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2046,6 +2073,7 @@ dependencies = [
name = "mullvad-api"
version = "0.0.0"
dependencies = [
+ "anyhow",
"async-trait",
"cbindgen",
"chrono",
@@ -2060,6 +2088,7 @@ dependencies = [
"mullvad-encrypted-dns-proxy",
"mullvad-fs",
"mullvad-types",
+ "mullvad-update",
"rustls-pemfile 2.1.3",
"serde",
"serde_json",
@@ -2072,6 +2101,7 @@ dependencies = [
"tokio-socks",
"tower 0.5.1",
"uuid",
+ "vec1",
]
[[package]]
@@ -2162,10 +2192,32 @@ dependencies = [
]
[[package]]
+name = "mullvad-update"
+version = "0.0.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "chrono",
+ "ed25519-dalek",
+ "hex",
+ "json-canon",
+ "mullvad-version",
+ "reqwest",
+ "serde",
+ "serde_json",
+ "sha2",
+ "thiserror 2.0.3",
+ "tokio",
+ "vec1",
+ "zeroize",
+]
+
+[[package]]
name = "mullvad-version"
version = "0.0.0"
dependencies = [
"regex-lite",
+ "serde",
]
[[package]]
@@ -2906,9 +2958,9 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
[[package]]
name = "reqwest"
-version = "0.12.7"
+version = "0.12.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
+checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f"
dependencies = [
"base64 0.22.0",
"bytes",
@@ -3116,6 +3168,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
+name = "ryu-js"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6518fc26bced4d53678a22d6e423e9d8716377def84545fe328236e3af070e7f"
+
+[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3252,6 +3310,17 @@ dependencies = [
]
[[package]]
+name = "sha2"
+version = "0.10.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
+dependencies = [
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+]
+
+[[package]]
name = "shadowsocks"
version = "1.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4248,6 +4317,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
+name = "vec1"
+version = "1.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eab68b56840f69efb0fefbe3ab6661499217ffdc58e2eef7c3f6f69835386322"
+
+[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"