| 
					
				 | 
			
			
				@@ -0,0 +1,188 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use crypto; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use crypto::mac::Mac; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use crypto::digest::Digest; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use dns_sd::DNSService; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use hyper; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use hyper::net::NetworkListener; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use num::BigUint; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use url; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use rand; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use rustc_serialize::base64::{self, ToBase64, FromBase64}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use std::collections::BTreeMap; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use std::io::{Read, Write}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use std::sync::{mpsc, Mutex}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use authentication::Credentials; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use diffie_hellman::{DH_GENERATOR, DH_PRIME}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+use util; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+struct ServerHandler { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    credentials_tx: Mutex<mpsc::Sender<Credentials>>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    private_key: BigUint, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public_key: BigUint, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    device_id: String, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    device_name: String, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+impl ServerHandler { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fn handle_get_info(&self, _params: &BTreeMap<String, String>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       mut response: hyper::server::Response<hyper::net::Fresh>) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let public_key = self.public_key.to_bytes_be() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                        .to_base64(base64::STANDARD); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let result = json!({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "status": 101, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "statusString": "ERROR-OK", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "spotifyError": 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "version": "2.1.0", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "deviceID": (self.device_id), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "remoteName": (self.device_name), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "activeUser": "", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "publicKey": (public_key), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "deviceType": "UNKNOWN", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "libraryVersion": "0.1.0", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "accountReq": "PREMIUM", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "brandDisplayName": "librespot", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "modelDisplayName": "librespot", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        *response.status_mut() = hyper::status::StatusCode::Ok; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        response.start().unwrap().write_all(result.to_string().as_bytes()).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fn handle_add_user(&self, params: &BTreeMap<String, String>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       mut response: hyper::server::Response<hyper::net::Fresh>) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let username = params.get("userName").unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let encrypted_blob = params.get("blob").unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let client_key = params.get("clientKey").unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let encrypted_blob = encrypted_blob.from_base64().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let client_key = client_key.from_base64().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let client_key = BigUint::from_bytes_be(&client_key); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let shared_key = util::powm(&client_key, &self.private_key, &DH_PRIME); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let iv = &encrypted_blob[0..16]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let encrypted = &encrypted_blob[16..encrypted_blob.len() - 20]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let cksum = &encrypted_blob[encrypted_blob.len() - 20..encrypted_blob.len()]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let base_key = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut data = [0u8; 20]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut h = crypto::sha1::Sha1::new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            h.input(&shared_key.to_bytes_be()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            h.result(&mut data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data[..16].to_owned() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let checksum_key = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut h = crypto::hmac::Hmac::new(crypto::sha1::Sha1::new(), &base_key); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            h.input("checksum".as_bytes()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            h.result().code().to_owned() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let encryption_key = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut h = crypto::hmac::Hmac::new(crypto::sha1::Sha1::new(), &base_key); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            h.input("encryption".as_bytes()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            h.result().code().to_owned() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let mac = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut h = crypto::hmac::Hmac::new(crypto::sha1::Sha1::new(), &checksum_key); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            h.input(encrypted); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            h.result().code().to_owned() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert_eq!(&mac[..], cksum); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let decrypted = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut data = vec![0u8; encrypted.len()]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut cipher = crypto::aes::ctr(crypto::aes::KeySize::KeySize128, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                              &encryption_key[0..16], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                              &iv); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cipher.process(&encrypted, &mut data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            String::from_utf8(data).unwrap() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let credentials = Credentials::with_blob(username.to_owned(), &decrypted, &self.device_id); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.credentials_tx.lock().unwrap().send(credentials).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let result = json!({ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "status": 101, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "spotifyError": 0, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "statusString": "ERROR-OK" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        *response.status_mut() = hyper::status::StatusCode::Ok; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        response.start().unwrap().write_all(result.to_string().as_bytes()).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fn not_found(&self, mut response: hyper::server::Response<hyper::net::Fresh>) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        *response.status_mut() = hyper::status::StatusCode::NotFound 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+impl hyper::server::Handler for ServerHandler { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fn handle<'a, 'k>(&'a self, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      mut request: hyper::server::Request<'a, 'k>, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      response: hyper::server::Response<'a, hyper::net::Fresh>) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if let hyper::uri::RequestUri::AbsolutePath(path) = request.uri.clone() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let (_, query, _) = url::parse_path(&path).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let mut params = query.map_or(vec![], |q| url::form_urlencoded::parse(q.as_bytes())) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                  .into_iter().collect::<BTreeMap<_,_>>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if request.method == hyper::method::Method::Post { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                let mut body = Vec::new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                request.read_to_end(&mut body).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                let form = url::form_urlencoded::parse(&body); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                params.extend(form); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            match params.get("action").map(AsRef::as_ref) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Some("getInfo") => self.handle_get_info(¶ms, response), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                Some("addUser") => self.handle_add_user(¶ms, response), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                _ => self.not_found(response), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.not_found(response) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+pub fn discovery_login(device_name: &str, device_id: &str) -> Result<Credentials, ()> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let (tx, rx) = mpsc::channel(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let key_data = util::rand_vec(&mut rand::thread_rng(), 95); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let private_key = BigUint::from_bytes_be(&key_data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let public_key = util::powm(&DH_GENERATOR, &private_key, &DH_PRIME); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let handler = ServerHandler { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        device_name: device_name.to_owned(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        device_id: device_id.to_owned(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private_key: private_key, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public_key: public_key, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        credentials_tx: Mutex::new(tx), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let mut listener = hyper::net::HttpListener::new("0.0.0.0:0").unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let port = listener.local_addr().unwrap().port(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let mut server = hyper::Server::new(listener).handle(handler).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let _svc = DNSService::register(Some(device_name), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    "_spotify-connect._tcp", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    port, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    &["VERSION=1.0", "CPath=/"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                    ).unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    let cred = rx.recv().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    server.close().unwrap(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Ok(cred) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 |