mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Add mobile client IDs and improve hash cash logic
This commit is contained in:
parent
111c7781d2
commit
10650712a7
2 changed files with 36 additions and 9 deletions
|
@ -3,6 +3,8 @@ use std::{fmt, path::PathBuf, str::FromStr};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) const KEYMASTER_CLIENT_ID: &str = "65b708073fc0480ea92a077233ca87bd";
|
pub(crate) const KEYMASTER_CLIENT_ID: &str = "65b708073fc0480ea92a077233ca87bd";
|
||||||
|
pub(crate) const ANDROID_CLIENT_ID: &str = "9a8d2f0ce77a4e248bb71fefcb557637";
|
||||||
|
pub(crate) const IOS_CLIENT_ID: &str = "58bd3c95768941ea9eb4350aaa033eb3";
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SessionConfig {
|
pub struct SessionConfig {
|
||||||
|
@ -16,8 +18,15 @@ pub struct SessionConfig {
|
||||||
impl Default for SessionConfig {
|
impl Default for SessionConfig {
|
||||||
fn default() -> SessionConfig {
|
fn default() -> SessionConfig {
|
||||||
let device_id = uuid::Uuid::new_v4().as_hyphenated().to_string();
|
let device_id = uuid::Uuid::new_v4().as_hyphenated().to_string();
|
||||||
|
let client_id = match std::env::consts::OS {
|
||||||
|
"android" => ANDROID_CLIENT_ID,
|
||||||
|
"ios" => IOS_CLIENT_ID,
|
||||||
|
_ => KEYMASTER_CLIENT_ID,
|
||||||
|
}
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
SessionConfig {
|
SessionConfig {
|
||||||
client_id: KEYMASTER_CLIENT_ID.to_owned(),
|
client_id,
|
||||||
device_id,
|
device_id,
|
||||||
proxy: None,
|
proxy: None,
|
||||||
ap_port: None,
|
ap_port: None,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
convert::TryInto,
|
convert::TryInto,
|
||||||
|
env::consts::OS,
|
||||||
fmt::Write,
|
fmt::Write,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
@ -22,7 +23,7 @@ use thiserror::Error;
|
||||||
use crate::{
|
use crate::{
|
||||||
apresolve::SocketAddress,
|
apresolve::SocketAddress,
|
||||||
cdn_url::CdnUrl,
|
cdn_url::CdnUrl,
|
||||||
config::KEYMASTER_CLIENT_ID,
|
config::SessionConfig,
|
||||||
error::ErrorKind,
|
error::ErrorKind,
|
||||||
protocol::{
|
protocol::{
|
||||||
canvaz::EntityCanvazRequest,
|
canvaz::EntityCanvazRequest,
|
||||||
|
@ -165,9 +166,17 @@ impl SpClient {
|
||||||
let client_data = message.mut_client_data();
|
let client_data = message.mut_client_data();
|
||||||
client_data.set_client_version(spotify_version());
|
client_data.set_client_version(spotify_version());
|
||||||
|
|
||||||
// using 9a8d2f0ce77a4e248bb71fefcb557637 on Android
|
// Current state of affairs: keymaster ID works on all tested platforms, but may be phased out,
|
||||||
// instead of the keymaster ID presents a hash cash challenge
|
// so it seems a good idea to mimick the real clients. `self.session().client_id()` returns the
|
||||||
client_data.set_client_id(KEYMASTER_CLIENT_ID.to_string());
|
// ID of the client that last connected, but requesting a client token with this ID only works
|
||||||
|
// on macOS and Windows. On Android and iOS we can send a platform-specific client ID and are
|
||||||
|
// then presented with a hash cash challenge. On Linux, we have to pass the old keymaster ID.
|
||||||
|
// We delegate most of this logic to `SessionConfig`.
|
||||||
|
let client_id = match OS {
|
||||||
|
"macos" | "windows" => self.session().client_id(),
|
||||||
|
_ => SessionConfig::default().client_id,
|
||||||
|
};
|
||||||
|
client_data.set_client_id(client_id);
|
||||||
|
|
||||||
let connectivity_data = client_data.mut_connectivity_sdk_data();
|
let connectivity_data = client_data.mut_connectivity_sdk_data();
|
||||||
connectivity_data.set_device_id(self.session().device_id().to_string());
|
connectivity_data.set_device_id(self.session().device_id().to_string());
|
||||||
|
@ -178,7 +187,7 @@ impl SpClient {
|
||||||
let os_version = sys.os_version().unwrap_or_else(|| String::from("0"));
|
let os_version = sys.os_version().unwrap_or_else(|| String::from("0"));
|
||||||
let kernel_version = sys.kernel_version().unwrap_or_else(|| String::from("0"));
|
let kernel_version = sys.kernel_version().unwrap_or_else(|| String::from("0"));
|
||||||
|
|
||||||
match std::env::consts::OS {
|
match OS {
|
||||||
"windows" => {
|
"windows" => {
|
||||||
let os_version = os_version.parse::<f32>().unwrap_or(10.) as i32;
|
let os_version = os_version.parse::<f32>().unwrap_or(10.) as i32;
|
||||||
let kernel_version = kernel_version.parse::<i32>().unwrap_or(21370);
|
let kernel_version = kernel_version.parse::<i32>().unwrap_or(21370);
|
||||||
|
@ -238,7 +247,7 @@ impl SpClient {
|
||||||
// or are presented a hash cash challenge to solve first
|
// or are presented a hash cash challenge to solve first
|
||||||
Some(ClientTokenResponseType::RESPONSE_GRANTED_TOKEN_RESPONSE) => break message,
|
Some(ClientTokenResponseType::RESPONSE_GRANTED_TOKEN_RESPONSE) => break message,
|
||||||
Some(ClientTokenResponseType::RESPONSE_CHALLENGES_RESPONSE) => {
|
Some(ClientTokenResponseType::RESPONSE_CHALLENGES_RESPONSE) => {
|
||||||
trace!("received a hash cash challenge");
|
trace!("received a hash cash challenge, solving...");
|
||||||
|
|
||||||
let challenges = message.get_challenges().clone();
|
let challenges = message.get_challenges().clone();
|
||||||
let state = challenges.get_state();
|
let state = challenges.get_state();
|
||||||
|
@ -248,7 +257,7 @@ impl SpClient {
|
||||||
let ctx = vec![];
|
let ctx = vec![];
|
||||||
let prefix = hex::decode(&hash_cash_challenge.prefix).map_err(|e| {
|
let prefix = hex::decode(&hash_cash_challenge.prefix).map_err(|e| {
|
||||||
Error::failed_precondition(format!(
|
Error::failed_precondition(format!(
|
||||||
"unable to decode Hashcash challenge: {}",
|
"unable to decode hash cash challenge: {}",
|
||||||
e
|
e
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
@ -274,7 +283,16 @@ impl SpClient {
|
||||||
challenge_answers.state = state.to_string();
|
challenge_answers.state = state.to_string();
|
||||||
challenge_answers.answers.push(challenge_answer);
|
challenge_answers.answers.push(challenge_answer);
|
||||||
|
|
||||||
response = self.client_token_request(&answer_message).await?;
|
trace!("answering hash cash challenge");
|
||||||
|
match self.client_token_request(&answer_message).await {
|
||||||
|
Ok(token) => response = token,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(Error::failed_precondition(format!(
|
||||||
|
"unable to solve this challenge: {}",
|
||||||
|
e
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// we should have been granted a token now
|
// we should have been granted a token now
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in a new issue