discovery.rs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. use aes_ctr::stream_cipher::generic_array::GenericArray;
  2. use aes_ctr::stream_cipher::{NewStreamCipher, SyncStreamCipher};
  3. use aes_ctr::Aes128Ctr;
  4. use base64;
  5. use futures::sync::mpsc;
  6. use futures::{Future, Poll, Stream};
  7. use hmac::{Hmac, Mac};
  8. use hyper::server::{Http, Request, Response, Service};
  9. use hyper::{self, Get, Post, StatusCode};
  10. use sha1::{Digest, Sha1};
  11. #[cfg(feature = "with-dns-sd")]
  12. use dns_sd::DNSService;
  13. #[cfg(not(feature = "with-dns-sd"))]
  14. use libmdns;
  15. use num_bigint::BigUint;
  16. use rand;
  17. use std::collections::BTreeMap;
  18. use std::io;
  19. use std::sync::Arc;
  20. use tokio_core::reactor::Handle;
  21. use url;
  22. use librespot_core::authentication::Credentials;
  23. use librespot_core::config::ConnectConfig;
  24. use librespot_core::diffie_hellman::{DH_GENERATOR, DH_PRIME};
  25. use librespot_core::util;
  26. type HmacSha1 = Hmac<Sha1>;
  27. #[derive(Clone)]
  28. struct Discovery(Arc<DiscoveryInner>);
  29. struct DiscoveryInner {
  30. config: ConnectConfig,
  31. device_id: String,
  32. private_key: BigUint,
  33. public_key: BigUint,
  34. tx: mpsc::UnboundedSender<Credentials>,
  35. }
  36. impl Discovery {
  37. fn new(
  38. config: ConnectConfig,
  39. device_id: String,
  40. ) -> (Discovery, mpsc::UnboundedReceiver<Credentials>) {
  41. let (tx, rx) = mpsc::unbounded();
  42. let key_data = util::rand_vec(&mut rand::thread_rng(), 95);
  43. let private_key = BigUint::from_bytes_be(&key_data);
  44. let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME);
  45. let discovery = Discovery(Arc::new(DiscoveryInner {
  46. config: config,
  47. device_id: device_id,
  48. private_key: private_key,
  49. public_key: public_key,
  50. tx: tx,
  51. }));
  52. (discovery, rx)
  53. }
  54. }
  55. impl Discovery {
  56. fn handle_get_info(
  57. &self,
  58. _params: &BTreeMap<String, String>,
  59. ) -> ::futures::Finished<Response, hyper::Error> {
  60. let public_key = self.0.public_key.to_bytes_be();
  61. let public_key = base64::encode(&public_key);
  62. let result = json!({
  63. "status": 101,
  64. "statusString": "ERROR-OK",
  65. "spotifyError": 0,
  66. "version": "2.1.0",
  67. "deviceID": (self.0.device_id),
  68. "remoteName": (self.0.config.name),
  69. "activeUser": "",
  70. "publicKey": (public_key),
  71. "deviceType": (self.0.config.device_type.to_string().to_uppercase()),
  72. "libraryVersion": "0.1.0",
  73. "accountReq": "PREMIUM",
  74. "brandDisplayName": "librespot",
  75. "modelDisplayName": "librespot",
  76. });
  77. let body = result.to_string();
  78. ::futures::finished(Response::new().with_body(body))
  79. }
  80. fn handle_add_user(
  81. &self,
  82. params: &BTreeMap<String, String>,
  83. ) -> ::futures::Finished<Response, hyper::Error> {
  84. let username = params.get("userName").unwrap();
  85. let encrypted_blob = params.get("blob").unwrap();
  86. let client_key = params.get("clientKey").unwrap();
  87. let encrypted_blob = base64::decode(encrypted_blob).unwrap();
  88. let client_key = base64::decode(client_key).unwrap();
  89. let client_key = BigUint::from_bytes_be(&client_key);
  90. let shared_key = util::powm(&client_key, &self.0.private_key, &DH_PRIME);
  91. let iv = &encrypted_blob[0..16];
  92. let encrypted = &encrypted_blob[16..encrypted_blob.len() - 20];
  93. let cksum = &encrypted_blob[encrypted_blob.len() - 20..encrypted_blob.len()];
  94. let base_key = Sha1::digest(&shared_key.to_bytes_be());
  95. let base_key = &base_key[..16];
  96. let checksum_key = {
  97. let mut h = HmacSha1::new_varkey(base_key).expect("HMAC can take key of any size");
  98. h.input(b"checksum");
  99. h.result().code()
  100. };
  101. let encryption_key = {
  102. let mut h = HmacSha1::new_varkey(&base_key).expect("HMAC can take key of any size");
  103. h.input(b"encryption");
  104. h.result().code()
  105. };
  106. let mut h = HmacSha1::new_varkey(&checksum_key).expect("HMAC can take key of any size");
  107. h.input(encrypted);
  108. if let Err(_) = h.verify(cksum) {
  109. warn!("Login error for user {:?}: MAC mismatch", username);
  110. let result = json!({
  111. "status": 102,
  112. "spotifyError": 1,
  113. "statusString": "ERROR-MAC"
  114. });
  115. let body = result.to_string();
  116. return ::futures::finished(Response::new().with_body(body));
  117. }
  118. let decrypted = {
  119. let mut data = encrypted.to_vec();
  120. let mut cipher = Aes128Ctr::new(
  121. &GenericArray::from_slice(&encryption_key[0..16]),
  122. &GenericArray::from_slice(iv),
  123. );
  124. cipher.apply_keystream(&mut data);
  125. String::from_utf8(data).unwrap()
  126. };
  127. let credentials =
  128. Credentials::with_blob(username.to_owned(), &decrypted, &self.0.device_id);
  129. self.0.tx.unbounded_send(credentials).unwrap();
  130. let result = json!({
  131. "status": 101,
  132. "spotifyError": 0,
  133. "statusString": "ERROR-OK"
  134. });
  135. let body = result.to_string();
  136. ::futures::finished(Response::new().with_body(body))
  137. }
  138. fn not_found(&self) -> ::futures::Finished<Response, hyper::Error> {
  139. ::futures::finished(Response::new().with_status(StatusCode::NotFound))
  140. }
  141. }
  142. impl Service for Discovery {
  143. type Request = Request;
  144. type Response = Response;
  145. type Error = hyper::Error;
  146. type Future = Box<dyn Future<Item = Response, Error = hyper::Error>>;
  147. fn call(&self, request: Request) -> Self::Future {
  148. let mut params = BTreeMap::new();
  149. let (method, uri, _, _, body) = request.deconstruct();
  150. if let Some(query) = uri.query() {
  151. params.extend(url::form_urlencoded::parse(query.as_bytes()).into_owned());
  152. }
  153. if method != Get {
  154. debug!("{:?} {:?} {:?}", method, uri.path(), params);
  155. }
  156. let this = self.clone();
  157. Box::new(
  158. body.fold(Vec::new(), |mut acc, chunk| {
  159. acc.extend_from_slice(chunk.as_ref());
  160. Ok::<_, hyper::Error>(acc)
  161. })
  162. .map(move |body| {
  163. params.extend(url::form_urlencoded::parse(&body).into_owned());
  164. params
  165. })
  166. .and_then(move |params| {
  167. match (method, params.get("action").map(AsRef::as_ref)) {
  168. (Get, Some("getInfo")) => this.handle_get_info(&params),
  169. (Post, Some("addUser")) => this.handle_add_user(&params),
  170. _ => this.not_found(),
  171. }
  172. }),
  173. )
  174. }
  175. }
  176. #[cfg(feature = "with-dns-sd")]
  177. pub struct DiscoveryStream {
  178. credentials: mpsc::UnboundedReceiver<Credentials>,
  179. _svc: DNSService,
  180. }
  181. #[cfg(not(feature = "with-dns-sd"))]
  182. pub struct DiscoveryStream {
  183. credentials: mpsc::UnboundedReceiver<Credentials>,
  184. _svc: libmdns::Service,
  185. }
  186. pub fn discovery(
  187. handle: &Handle,
  188. config: ConnectConfig,
  189. device_id: String,
  190. port: u16,
  191. ) -> io::Result<DiscoveryStream> {
  192. let (discovery, creds_rx) = Discovery::new(config.clone(), device_id);
  193. let serve = {
  194. let http = Http::new();
  195. http.serve_addr_handle(
  196. &format!("0.0.0.0:{}", port).parse().unwrap(),
  197. &handle,
  198. move || Ok(discovery.clone()),
  199. )
  200. .unwrap()
  201. };
  202. let s_port = serve.incoming_ref().local_addr().port();
  203. debug!("Zeroconf server listening on 0.0.0.0:{}", s_port);
  204. let server_future = {
  205. let handle = handle.clone();
  206. serve
  207. .for_each(move |connection| {
  208. handle.spawn(connection.then(|_| Ok(())));
  209. Ok(())
  210. })
  211. .then(|_| Ok(()))
  212. };
  213. handle.spawn(server_future);
  214. #[cfg(feature = "with-dns-sd")]
  215. let svc = DNSService::register(
  216. Some(&*config.name),
  217. "_spotify-connect._tcp",
  218. None,
  219. None,
  220. s_port,
  221. &["VERSION=1.0", "CPath=/"],
  222. )
  223. .unwrap();
  224. #[cfg(not(feature = "with-dns-sd"))]
  225. let responder = libmdns::Responder::spawn(&handle)?;
  226. #[cfg(not(feature = "with-dns-sd"))]
  227. let svc = responder.register(
  228. "_spotify-connect._tcp".to_owned(),
  229. config.name,
  230. s_port,
  231. &["VERSION=1.0", "CPath=/"],
  232. );
  233. Ok(DiscoveryStream {
  234. credentials: creds_rx,
  235. _svc: svc,
  236. })
  237. }
  238. impl Stream for DiscoveryStream {
  239. type Item = Credentials;
  240. type Error = ();
  241. fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
  242. self.credentials.poll()
  243. }
  244. }