Skip to content

Commit 4256621

Browse files
Replace custom protobuf handlers with librespot-protocol (#714)
Co-authored-by: Jackson Goode <54308792+jacksongoode@users.noreply.github.com>
1 parent 3846f9c commit 4256621

31 files changed

+118
-5392
lines changed

Cargo.lock

Lines changed: 0 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
22
resolver = "2"
3-
members = ["psst-protocol", "psst-core", "psst-cli", "psst-gui"]
3+
members = ["psst-core", "psst-cli", "psst-gui"]
44

55
[profile.dev]
66
opt-level = 1

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ Here's the basic project structure:
164164
- `/psst-core` - Core library, takes care of Spotify TCP session, audio file retrieval, decoding, audio output, playback queue, etc.
165165
- `/psst-gui` - GUI application built with [Druid](https://github.com/linebender/druid)
166166
- `/psst-cli` - Example CLI that plays a track. Credentials must be configured in the code.
167-
- `/psst-protocol` - Internal Protobuf definitions used for Spotify communication.
168167
169168
## Privacy Policy
170169

psst-core/Cargo.toml

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,6 @@ gix-config = "0.45.1"
1010
time = { version = "0.3.36", features = ["local-offset"] }
1111

1212
[dependencies]
13-
psst-protocol = { path = "../psst-protocol" }
14-
rustfm-scrobble = "1.1.1"
15-
16-
# Login5 dependencies
17-
librespot-protocol = "0.7.1"
18-
protobuf = "3"
19-
sysinfo = "0.35.0"
20-
data-encoding = "2.9"
2113

2214
# Common
2315
byteorder = { version = "1.5.0" }
@@ -28,13 +20,17 @@ num-bigint = { version = "0.4.6", features = ["rand"] }
2820
num-traits = { version = "0.2.19" }
2921
oauth2 = { version = "4.4.2" }
3022
parking_lot = { version = "0.12.3" }
31-
quick-protobuf = { version = "0.8.1" }
23+
librespot-protocol = "0.7.1"
24+
protobuf = "3"
25+
sysinfo = "0.35.0"
26+
data-encoding = "2.9"
3227
rand = { version = "0.9.1" }
3328
rangemap = { version = "1.5.1" }
3429
serde = { version = "1.0.210", features = ["derive"] }
3530
serde_json = { version = "1.0.132" }
3631
socks = { version = "0.3.4" }
3732
tempfile = { version = "3.13.0" }
33+
rustfm-scrobble = "1.1.1"
3834
ureq = { version = "3.0.11", features = ["json"] }
3935
url = { version = "2.5.2" }
4036

psst-core/src/cache.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ use crate::{
88
audio::decrypt::AudioKey,
99
error::Error,
1010
item_id::{FileId, ItemId},
11-
protocol::metadata::{Episode, Track},
12-
util::{deserialize_protobuf, serialize_protobuf},
1311
};
1412

13+
use librespot_protocol::metadata::{Episode, Track};
14+
use protobuf::Message;
15+
1516
pub type CacheHandle = Arc<Cache>;
1617

1718
#[derive(Debug)]
@@ -61,12 +62,12 @@ impl Cache {
6162
impl Cache {
6263
pub fn get_track(&self, item_id: ItemId) -> Option<Track> {
6364
let buf = fs::read(self.track_path(item_id)).ok()?;
64-
deserialize_protobuf(&buf).ok()
65+
Track::parse_from_bytes(&buf).ok()
6566
}
6667

6768
pub fn save_track(&self, item_id: ItemId, track: &Track) -> Result<(), Error> {
6869
log::debug!("saving track to cache: {item_id:?}");
69-
fs::write(self.track_path(item_id), serialize_protobuf(track)?)?;
70+
fs::write(self.track_path(item_id), track.write_to_bytes()?)?;
7071
Ok(())
7172
}
7273

@@ -79,12 +80,12 @@ impl Cache {
7980
impl Cache {
8081
pub fn get_episode(&self, item_id: ItemId) -> Option<Episode> {
8182
let buf = fs::read(self.episode_path(item_id)).ok()?;
82-
deserialize_protobuf(&buf).ok()
83+
Episode::parse_from_bytes(&buf).ok()
8384
}
8485

8586
pub fn save_episode(&self, item_id: ItemId, episode: &Episode) -> Result<(), Error> {
8687
log::debug!("saving episode to cache: {item_id:?}");
87-
fs::write(self.episode_path(item_id), serialize_protobuf(episode)?)?;
88+
fs::write(self.episode_path(item_id), episode.write_to_bytes()?)?;
8889
Ok(())
8990
}
9091

psst-core/src/connection/mod.rs

Lines changed: 69 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@ use crate::{
2121
shannon_codec::{ShannonDecoder, ShannonEncoder, ShannonMsg},
2222
},
2323
error::Error,
24-
protocol::authentication::AuthenticationType,
25-
util::{
26-
default_ureq_agent_builder, deserialize_protobuf, serialize_protobuf, NET_CONNECT_TIMEOUT,
27-
NET_IO_TIMEOUT,
28-
},
24+
util::{default_ureq_agent_builder, NET_CONNECT_TIMEOUT, NET_IO_TIMEOUT},
2925
};
3026

27+
use librespot_protocol::authentication::AuthenticationType;
28+
use protobuf::{Enum, Message, MessageField, SpecialFields};
29+
3130
// Device ID used for authentication message.
3231
const DEVICE_ID: &str = "Psst";
3332

@@ -76,7 +75,7 @@ impl From<SerializedCredentials> for Credentials {
7675
Self {
7776
username: Some(value.username),
7877
auth_data: value.auth_data.into_bytes(),
79-
auth_type: value.auth_type.into(),
78+
auth_type: AuthenticationType::from_i32(value.auth_type).unwrap_or_default(),
8079
}
8180
}
8281
}
@@ -215,7 +214,7 @@ impl Transport {
215214
}
216215

217216
pub fn exchange_keys(mut stream: TcpStream) -> Result<Self, Error> {
218-
use crate::protocol::keyexchange::APResponseMessage;
217+
use librespot_protocol::keyexchange::APResponseMessage;
219218

220219
let local_keys = DHLocalKeys::random();
221220

@@ -232,17 +231,18 @@ impl Transport {
232231
// hashed together with the shared secret to make a key pair).
233232
log::trace!("waiting for AP response");
234233
let apresp_packet = read_packet(&mut stream)?;
235-
let apresp: APResponseMessage = deserialize_protobuf(&apresp_packet[4..])?;
234+
let apresp = APResponseMessage::parse_from_bytes(&apresp_packet[4..])?;
236235
log::trace!("received AP response");
237236

238237
// Compute the challenge response and the sending/receiving keys.
239-
let remote_key = &apresp
238+
let remote_key = apresp
240239
.challenge
241-
.expect("Missing data")
242240
.login_crypto_challenge
243241
.diffie_hellman
244-
.expect("Missing data")
245-
.gs;
242+
.gs
243+
.as_ref()
244+
.expect("Missing data");
245+
246246
let (challenge, send_key, recv_key) = compute_keys(
247247
&local_keys.shared_secret(remote_key),
248248
&hello_packet,
@@ -268,7 +268,7 @@ impl Transport {
268268
}
269269

270270
pub fn authenticate(&mut self, credentials: Credentials) -> Result<Credentials, Error> {
271-
use crate::protocol::{authentication::APWelcome, keyexchange::APLoginFailed};
271+
use librespot_protocol::{authentication::APWelcome, keyexchange::APLoginFailed};
272272

273273
// Send a login request with the client credentials.
274274
let request = client_response_encrypted(credentials);
@@ -279,19 +279,20 @@ impl Transport {
279279

280280
match response.cmd {
281281
ShannonMsg::AP_WELCOME => {
282-
let welcome_data: APWelcome =
283-
deserialize_protobuf(&response.payload).expect("Missing data");
282+
let welcome_data =
283+
APWelcome::parse_from_bytes(&response.payload).expect("Missing data");
284+
284285
Ok(Credentials {
285-
username: Some(welcome_data.canonical_username),
286-
auth_data: welcome_data.reusable_auth_credentials,
287-
auth_type: welcome_data.reusable_auth_credentials_type,
286+
username: Some(welcome_data.canonical_username().to_string()),
287+
auth_data: welcome_data.reusable_auth_credentials().to_vec(),
288+
auth_type: welcome_data.reusable_auth_credentials_type(),
288289
})
289290
}
290291
ShannonMsg::AUTH_FAILURE => {
291-
let error_data: APLoginFailed =
292-
deserialize_protobuf(&response.payload).expect("Missing data");
292+
let error_data =
293+
APLoginFailed::parse_from_bytes(&response.payload).expect("Missing data");
293294
Err(Error::AuthFailed {
294-
code: error_data.error_code as _,
295+
code: error_data.error_code() as _,
295296
})
296297
}
297298
_ => {
@@ -321,44 +322,57 @@ fn make_packet(prefix: &[u8], data: &[u8]) -> Vec<u8> {
321322
}
322323

323324
fn client_hello(public_key: Vec<u8>, nonce: Vec<u8>) -> Vec<u8> {
324-
use crate::protocol::keyexchange::*;
325+
use librespot_protocol::keyexchange::*;
325326

326327
let hello = ClientHello {
327-
build_info: BuildInfo {
328-
platform: Platform::PLATFORM_LINUX_X86,
329-
product: Product::PRODUCT_PARTNER,
328+
build_info: MessageField::some(BuildInfo {
329+
platform: Some(Platform::PLATFORM_LINUX_X86.into()),
330+
product: Some(Product::PRODUCT_PARTNER.into()),
330331
product_flags: vec![],
331-
version: 109_800_078,
332-
},
333-
cryptosuites_supported: vec![Cryptosuite::CRYPTO_SUITE_SHANNON],
332+
version: Some(109_800_078),
333+
special_fields: SpecialFields::new(),
334+
}),
335+
cryptosuites_supported: vec![Cryptosuite::CRYPTO_SUITE_SHANNON.into()],
334336
fingerprints_supported: vec![],
335337
powschemes_supported: vec![],
336-
login_crypto_hello: LoginCryptoHelloUnion {
337-
diffie_hellman: Some(LoginCryptoDiffieHellmanHello {
338-
gc: public_key,
339-
server_keys_known: 1,
338+
login_crypto_hello: MessageField::some(LoginCryptoHelloUnion {
339+
diffie_hellman: MessageField::some(LoginCryptoDiffieHellmanHello {
340+
gc: Some(public_key),
341+
server_keys_known: Some(1),
342+
special_fields: SpecialFields::new(),
340343
}),
341-
},
342-
client_nonce: nonce,
344+
special_fields: SpecialFields::new(),
345+
}),
346+
client_nonce: Some(nonce),
343347
padding: Some(vec![0x1e]),
344-
feature_set: None,
348+
feature_set: None.into(),
349+
special_fields: SpecialFields::new(),
345350
};
346351

347-
serialize_protobuf(&hello).expect("Failed to serialize")
352+
hello
353+
.write_to_bytes()
354+
.expect("Failed to serialize client hello")
348355
}
349356

350357
fn client_response_plaintext(challenge: Vec<u8>) -> Vec<u8> {
351-
use crate::protocol::keyexchange::*;
358+
use librespot_protocol::keyexchange::*;
352359

353360
let response = ClientResponsePlaintext {
354-
login_crypto_response: LoginCryptoResponseUnion {
355-
diffie_hellman: Some(LoginCryptoDiffieHellmanResponse { hmac: challenge }),
356-
},
357-
pow_response: PoWResponseUnion::default(),
358-
crypto_response: CryptoResponseUnion::default(),
361+
login_crypto_response: MessageField::some(LoginCryptoResponseUnion {
362+
diffie_hellman: MessageField::some(LoginCryptoDiffieHellmanResponse {
363+
hmac: Some(challenge),
364+
special_fields: SpecialFields::new(),
365+
}),
366+
special_fields: SpecialFields::new(),
367+
}),
368+
pow_response: MessageField::some(PoWResponseUnion::default()),
369+
crypto_response: MessageField::some(CryptoResponseUnion::default()),
370+
special_fields: SpecialFields::new(),
359371
};
360372

361-
serialize_protobuf(&response).expect("Failed to serialize")
373+
response
374+
.write_to_bytes()
375+
.expect("Failed to serialize client response")
362376
}
363377

364378
fn compute_keys(
@@ -389,22 +403,27 @@ fn compute_keys(
389403
}
390404

391405
fn client_response_encrypted(credentials: Credentials) -> ShannonMsg {
392-
use crate::protocol::authentication::{ClientResponseEncrypted, LoginCredentials, SystemInfo};
406+
use librespot_protocol::authentication::{
407+
ClientResponseEncrypted, LoginCredentials, SystemInfo, Os, CpuFamily
408+
};
393409

394410
let response = ClientResponseEncrypted {
395-
login_credentials: LoginCredentials {
411+
login_credentials: MessageField::some(LoginCredentials {
396412
username: credentials.username,
397413
auth_data: Some(credentials.auth_data),
398-
typ: credentials.auth_type,
399-
},
400-
system_info: SystemInfo {
414+
typ: Some(credentials.auth_type.into()),
415+
special_fields: SpecialFields::new(),
416+
}),
417+
system_info: MessageField::some(SystemInfo {
401418
device_id: Some(DEVICE_ID.to_string()),
402419
system_information_string: Some("librespot_but_actually_psst".to_string()),
420+
os: Some(Os::default().into()),
421+
cpu_family: Some(CpuFamily::default().into()),
403422
..SystemInfo::default()
404-
},
423+
}),
405424
..ClientResponseEncrypted::default()
406425
};
407426

408-
let buf = serialize_protobuf(&response).expect("Failed to serialize");
427+
let buf = response.write_to_bytes().expect("Failed to serialize");
409428
ShannonMsg::new(ShannonMsg::LOGIN, buf)
410429
}

psst-core/src/lib.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,3 @@ pub mod player;
2020
pub mod session;
2121
pub mod system_info;
2222
pub mod util;
23-
24-
pub use psst_protocol as protocol;

0 commit comments

Comments
 (0)