session.rs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. use crypto::digest::Digest;
  2. use crypto::mac::Mac;
  3. use crypto::sha1::Sha1;
  4. use crypto::hmac::Hmac;
  5. use eventual::Future;
  6. use protobuf::{self, Message};
  7. use rand::thread_rng;
  8. use std::io::Write;
  9. use std::path::PathBuf;
  10. use std::sync::{Mutex, RwLock, Arc, mpsc};
  11. use audio_key::{AudioKeyManager, AudioKey, AudioKeyError};
  12. use audio_file::{AudioFileManager, AudioFile};
  13. use connection::{self, PlainConnection, CipherConnection};
  14. use connection::PacketHandler;
  15. use diffie_hellman::DHLocalKeys;
  16. use librespot_protocol as protocol;
  17. use mercury::{MercuryManager, MercuryRequest, MercuryResponse};
  18. use metadata::{MetadataManager, MetadataRef, MetadataTrait};
  19. use stream::{StreamManager, StreamEvent};
  20. use util::{SpotifyId, FileId, mkdir_existing};
  21. use util;
  22. pub struct Config {
  23. pub application_key: Vec<u8>,
  24. pub user_agent: String,
  25. pub device_name: String,
  26. pub cache_location: PathBuf,
  27. }
  28. pub struct SessionData {
  29. pub country: String,
  30. pub canonical_username: String,
  31. pub device_id: String,
  32. }
  33. pub struct SessionInternal {
  34. pub config: Config,
  35. pub data: RwLock<SessionData>,
  36. mercury: Mutex<MercuryManager>,
  37. metadata: Mutex<MetadataManager>,
  38. stream: Mutex<StreamManager>,
  39. audio_key: Mutex<AudioKeyManager>,
  40. audio_file: Mutex<AudioFileManager>,
  41. rx_connection: Mutex<Option<CipherConnection>>,
  42. tx_connection: Mutex<Option<CipherConnection>>,
  43. }
  44. #[derive(Clone)]
  45. pub struct Session(pub Arc<SessionInternal>);
  46. impl Session {
  47. pub fn new(config: Config) -> Session {
  48. mkdir_existing(&config.cache_location).unwrap();
  49. let device_id = {
  50. let mut h = Sha1::new();
  51. h.input_str(&config.device_name);
  52. h.result_str()
  53. };
  54. Session(Arc::new(SessionInternal {
  55. config: config,
  56. data: RwLock::new(SessionData {
  57. country: String::new(),
  58. canonical_username: String::new(),
  59. device_id: device_id,
  60. }),
  61. rx_connection: Mutex::new(None),
  62. tx_connection: Mutex::new(None),
  63. mercury: Mutex::new(MercuryManager::new()),
  64. metadata: Mutex::new(MetadataManager::new()),
  65. stream: Mutex::new(StreamManager::new()),
  66. audio_key: Mutex::new(AudioKeyManager::new()),
  67. audio_file: Mutex::new(AudioFileManager::new()),
  68. }))
  69. }
  70. pub fn connect(&self) {
  71. let local_keys = DHLocalKeys::random(&mut thread_rng());
  72. let mut connection = PlainConnection::connect().unwrap();
  73. let request = protobuf_init!(protocol::keyexchange::ClientHello::new(), {
  74. build_info => {
  75. product: protocol::keyexchange::Product::PRODUCT_LIBSPOTIFY_EMBEDDED,
  76. platform: protocol::keyexchange::Platform::PLATFORM_LINUX_X86,
  77. version: 0x10800000000,
  78. },
  79. /*
  80. fingerprints_supported => [
  81. protocol::keyexchange::Fingerprint::FINGERPRINT_GRAIN
  82. ],
  83. */
  84. cryptosuites_supported => [
  85. protocol::keyexchange::Cryptosuite::CRYPTO_SUITE_SHANNON,
  86. //protocol::keyexchange::Cryptosuite::CRYPTO_SUITE_RC4_SHA1_HMAC
  87. ],
  88. /*
  89. powschemes_supported => [
  90. protocol::keyexchange::Powscheme::POW_HASH_CASH
  91. ],
  92. */
  93. login_crypto_hello.diffie_hellman => {
  94. gc: local_keys.public_key(),
  95. server_keys_known: 1,
  96. },
  97. client_nonce: util::rand_vec(&mut thread_rng(), 0x10),
  98. padding: vec![0x1e],
  99. feature_set => {
  100. autoupdate2: true,
  101. }
  102. });
  103. let init_client_packet =
  104. connection.send_packet_prefix(&[0,4], &request.write_to_bytes().unwrap()).unwrap();
  105. let init_server_packet =
  106. connection.recv_packet().unwrap();
  107. let response : protocol::keyexchange::APResponseMessage =
  108. protobuf::parse_from_bytes(&init_server_packet[4..]).unwrap();
  109. let remote_key = response.get_challenge()
  110. .get_login_crypto_challenge()
  111. .get_diffie_hellman()
  112. .get_gs();
  113. let shared_secret = local_keys.shared_secret(remote_key);
  114. let (challenge, send_key, recv_key) = {
  115. let mut data = Vec::with_capacity(0x64);
  116. let mut mac = Hmac::new(Sha1::new(), &shared_secret);
  117. for i in 1..6 {
  118. mac.input(&init_client_packet);
  119. mac.input(&init_server_packet);
  120. mac.input(&[i]);
  121. data.write(&mac.result().code()).unwrap();
  122. mac.reset();
  123. }
  124. mac = Hmac::new(Sha1::new(), &data[..0x14]);
  125. mac.input(&init_client_packet);
  126. mac.input(&init_server_packet);
  127. (mac.result().code().to_vec(),
  128. data[0x14..0x34].to_vec(),
  129. data[0x34..0x54].to_vec())
  130. };
  131. let packet = protobuf_init!(protocol::keyexchange::ClientResponsePlaintext::new(), {
  132. login_crypto_response.diffie_hellman => {
  133. hmac: challenge
  134. },
  135. pow_response => {},
  136. crypto_response => {},
  137. });
  138. connection.send_packet(&packet.write_to_bytes().unwrap()).unwrap();
  139. let cipher_connection = CipherConnection::new(
  140. connection.into_stream(),
  141. &send_key,
  142. &recv_key);
  143. *self.0.rx_connection.lock().unwrap() = Some(cipher_connection.clone());
  144. *self.0.tx_connection.lock().unwrap() = Some(cipher_connection);
  145. }
  146. pub fn poll(&self) {
  147. let (cmd, data) = self.recv();
  148. match cmd {
  149. 0x4 => self.send_packet(0x49, &data).unwrap(),
  150. 0x4a => (),
  151. 0x9 => self.0.stream.lock().unwrap().handle(cmd, data),
  152. 0xd | 0xe => self.0.audio_key.lock().unwrap().handle(cmd, data),
  153. 0x1b => {
  154. self.0.data.write().unwrap().country =
  155. String::from_utf8(data).unwrap();
  156. },
  157. 0xb2...0xb6 => self.0.mercury.lock().unwrap().handle(cmd, data),
  158. _ => ()
  159. }
  160. }
  161. pub fn recv(&self) -> (u8, Vec<u8>) {
  162. self.0.rx_connection.lock().unwrap().as_mut().unwrap().recv_packet().unwrap()
  163. }
  164. pub fn send_packet(&self, cmd: u8, data: &[u8]) -> connection::Result<()> {
  165. self.0.tx_connection.lock().unwrap().as_mut().unwrap().send_packet(cmd, data)
  166. }
  167. pub fn audio_key(&self, track: SpotifyId, file: FileId) -> Future<AudioKey, AudioKeyError> {
  168. self.0.audio_key.lock().unwrap().request(self, track, file)
  169. }
  170. pub fn audio_file(&self, file: FileId) -> AudioFile {
  171. self.0.audio_file.lock().unwrap().request(self, file)
  172. }
  173. pub fn stream(&self, file: FileId, offset: u32, size: u32) -> mpsc::Receiver<StreamEvent> {
  174. self.0.stream.lock().unwrap().request(self, file, offset, size)
  175. }
  176. pub fn metadata<T: MetadataTrait>(&self, id: SpotifyId) -> MetadataRef<T> {
  177. self.0.metadata.lock().unwrap().get(self, id)
  178. }
  179. pub fn mercury(&self, req: MercuryRequest) -> Future<MercuryResponse, ()> {
  180. self.0.mercury.lock().unwrap().request(self, req)
  181. }
  182. pub fn mercury_sub(&self, uri: String) -> mpsc::Receiver<MercuryResponse> {
  183. self.0.mercury.lock().unwrap().subscribe(self, uri)
  184. }
  185. }