Ejemplo n.º 1
0
        public async Task QueryDataInsecure()
        {
            ServerInfo = await NvHttp.ServerInfo();

            Online = true;
            InitializeSecureClient();
        }
Ejemplo n.º 2
0
 public NvStreamDevice(IPAddress ipAddress, CryptoProvider cryptoProvider)
 {
     IPAddress      = ipAddress;
     CryptoProvider = cryptoProvider;
     NvHttp         = new NvHttp(new Uri($"http://{IPAddress}:{HTTP_PORT}/"));
     Online         = false;
 }
Ejemplo n.º 3
0
        /// <summary>
        /// Create start HTTP request
        /// </summary>
        private async Task<bool> StartOrResumeApp(NvHttp nv, MoonlightStreamConfiguration streamConfig)
        {
            XmlQuery serverInfo = new XmlQuery(nv.BaseUrl + "/serverinfo?uniqueid=" + nv.GetUniqueId());
            string currentGameString = await serverInfo.ReadXmlElement("currentgame");
            if (currentGameString == null)
            {
                return false;
            }

            string versionString = await serverInfo.ReadXmlElement("appversion");
            if (versionString == null)
            {
                return false;
            }

            serverMajorVersion = Convert.ToInt32(versionString.Substring(0, 1));

            byte[] aesIv = streamConfig.GetRiAesIv();
            int riKeyId =
                (int)(((aesIv[0] << 24) & 0xFF000000U) |
                ((aesIv[1] << 16) & 0xFF0000U) |
                ((aesIv[2] << 8) & 0xFF00U) |
                (aesIv[3] & 0xFFU));
            string riConfigString =
                "&rikey=" + PairingCryptoHelpers.BytesToHex(streamConfig.GetRiAesKey()) +
                "&rikeyid=" + riKeyId;

            // Launch a new game if nothing is running
            if (currentGameString == null || currentGameString.Equals("0"))
            {
                XmlQuery x = new XmlQuery(nv.BaseUrl + "/launch?uniqueid=" + nv.GetUniqueId() + "&appid=" + context.appId +
                    "&mode=" + streamConfig.GetWidth() + "x" + streamConfig.GetHeight() + "x" + streamConfig.GetFps() +
                    "&additionalStates=1&sops=1" + // FIXME: make sops configurable
                    riConfigString);

                string sessionStr = await x.ReadXmlElement("gamesession");
                if (sessionStr == null || sessionStr.Equals("0"))
                {
                    return false;
                }

                return true;
            }
            else
            {
                // A game was already running, so resume it
                // FIXME: Quit and relaunch if it's not the game we came to start
                XmlQuery x = new XmlQuery(nv.BaseUrl + "/resume?uniqueid=" + nv.GetUniqueId() + riConfigString);

                string resumeStr = await x.ReadXmlElement("resume");
                if (resumeStr == null || resumeStr.Equals("0"))
                {
                    return false;
                }

                return true;
            }
        }
Ejemplo n.º 4
0
        public async Task <bool> Pair()
        {
            // Generate salt for hashing the pin
            byte[] salt = CryptoProvider.GenerateRandomBytes(16);

            // Combine sal and pin and generate aes key from them
            string pin = CryptoProvider.GeneratePin();

            byte[]       saltedPin = CryptoProvider.SaltPin(salt, pin);
            KeyParameter aesKey    = CryptoProvider.GenerateAesKey(EnhancedSecurity, saltedPin);

            // Send the salt and get server cert. This doesn't have read timeout
            // because the user must enter the PIN before the server responds
            NvPair getServerCertResponse = await NvHttp.GetServerCert(salt, CryptoProvider.GetCertificatePem());
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Quit Game Event Handler
        /// </summary>
        private async Task QuitGame_Common()
        {
            Computer selected = (Computer)computerPicker.SelectedItem;

            // User hasn't selected anything or selected the placeholder
            if (selected == null || selected.IpAddress == null)
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "No machine selected", "Quit Failed");
                return;
            }

            PairingManager p = new PairingManager(selected);

            if (await p.QueryPairState() != true)
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "Device not paired", "Quit Failed");
                return;
            }

            try
            {
                NvHttp   nv        = new NvHttp(selected.IpAddress);
                XmlQuery quit      = new XmlQuery(nv.BaseUrl + "/cancel?uniqueid=" + nv.GetUniqueId());
                string   cancelled = await quit.ReadXmlElement("cancel");

                if (cancelled == "1")
                {
                    DialogUtils.DisplayDialog(this.Dispatcher, "Successfully Quit Game", "Quit Game");
                    return;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            DialogUtils.DisplayDialog(this.Dispatcher, "Unable to quit", "Quit Game");
        }
Ejemplo n.º 6
0
 private void InitializeSecureClient()
 {
     SecureNvHttp = new NvHttp(new Uri($"https://{IPAddress}:{ServerInfo.HttpsPort}/"), NvHttp.Uuid);
 }
Ejemplo n.º 7
0
 /// <summary>
 /// Constructor that sets nv 
 /// </summary>
 /// <param name="nv">The NvHttp Object</param>
 public PairingManager(Computer computer)
 {
     this.nv = new NvHttp(computer.IpAddress); 
 }
Ejemplo n.º 8
0
 /// <summary>
 /// Unpair from the device
 /// </summary>
 private static async Task Unpair(NvHttp nv)
 {
     XmlQuery unpair;
     unpair = new XmlQuery(nv.BaseUrl + "/unpair?uniqueid=" + nv.GetUniqueId());
     await unpair.Run();
 }
Ejemplo n.º 9
0
        public static async Task<bool> PerformPairingHandshake(CoreDispatcher uiDispatcher, WindowsCryptoProvider provider, NvHttp nv, string uniqueId)
        {
            string result;

            // Generate a salt for hashing the PIN
            byte[] salt = GenerateRandomBytes(16);

            string pin = new Random().Next(9999).ToString("D4");
            
            // Combine the salt and pin, then create an AES key from them
            byte[] saltAndPin = SaltPin(salt, pin);
            CryptographicKey aesKey = GenerateAesKey(saltAndPin);

            // Send the salt and get the server cert
            DialogUtils.DisplayDialog(uiDispatcher, "Enter the following PIN on the host PC: " + pin, "Enter PIN");

            // User will need to close dialog themselves
            XmlQuery getServerCert = new XmlQuery(nv.BaseUrl + "/pair?uniqueid=" + uniqueId +
                "&devicename=roth&updateState=1&phrase=getservercert&salt=" + BytesToHex(salt) + "&clientcert=" + BytesToHex(await provider.GetPemCertBytes()));
            result = await getServerCert.ReadXmlElement("paired");
            if (result == null || !result.Equals("1"))
            {
                await Unpair(nv);
                return false; 
            }

            X509Certificate serverCert = await ExtractPlainCert(getServerCert, "plaincert");

            // Generate a random challenge and encrypt it with our AES key
		    byte[] randomChallenge = GenerateRandomBytes(16);
            Debug.WriteLine("Client challenge: " + BytesToHex(randomChallenge));
		    byte[] encryptedChallenge = EncryptAes(randomChallenge, aesKey);

		    // Send the encrypted challenge to the server
		    XmlQuery challengeResp = new XmlQuery(nv.BaseUrl + 
				    "/pair?uniqueid="+uniqueId+"&devicename=roth&updateState=1&clientchallenge="+BytesToHex(encryptedChallenge));
            // If we're not paired, there's a problem. 
            result = await challengeResp.ReadXmlElement("paired");
		    if (result == null || !result.Equals("1")) {
                await Unpair(nv); 
			    return false;
		    }

            // Decode the server's response and subsequent challenge
            byte[] encServerChallengeResponse = HexToBytes(await challengeResp.ReadXmlElement("challengeresponse"));
            byte[] decServerChallengeResponse = DecryptAes(encServerChallengeResponse, aesKey);

            byte[] serverResponse = new byte[20], serverChallenge = new byte[16];
            Array.Copy(decServerChallengeResponse, serverResponse, serverResponse.Length);
            Array.Copy(decServerChallengeResponse, 20, serverChallenge, 0, serverChallenge.Length);
            Debug.WriteLine("serverResponse: " + BytesToHex(serverResponse));
            Debug.WriteLine("server challenge: " + BytesToHex(serverChallenge));

            // Using another 16 bytes secret, compute a challenge response hash using the secret, our cert sig, and the challenge
            byte[] clientSecret = GenerateRandomBytes(16);
            Debug.WriteLine("Client secret: " + BytesToHex(clientSecret));
            Debug.WriteLine("Client sig: " + BytesToHex((await provider.GetClientCertificate()).GetSignature()));

            byte[] challengeRespHash = ToSHA1Bytes(concatBytes(concatBytes(serverChallenge, (await provider.GetClientCertificate()).GetSignature()), clientSecret));
            Debug.WriteLine("Challenge SHA 1: " + BytesToHex(challengeRespHash));
            byte[] challengeRespEncrypted = EncryptAes(challengeRespHash, aesKey);
            XmlQuery secretResp = new XmlQuery(nv.BaseUrl +
                    "/pair?uniqueid=" + uniqueId + "&devicename=roth&updateState=1&serverchallengeresp=" + BytesToHex(challengeRespEncrypted));
            result = await secretResp.ReadXmlElement("paired");
            if (result == null || !result.Equals("1"))
            {
                await Unpair(nv); 
                return false;
            }

            // Get the server's signed secret
            byte[] serverSecretResp = HexToBytes(await secretResp.ReadXmlElement("pairingsecret"));
            byte[] serverSecret = new byte[16]; byte[] serverSignature = new byte[256]; 
            Array.Copy(serverSecretResp, 0, serverSecret, 0, 16);
            Array.Copy(serverSecretResp, 16, serverSignature, 0, 256);

            // Ensure the authenticity of the data
            if (!VerifySignature(serverSecret, serverSignature, serverCert))
            {
                // Cancel the pairing process
                await Unpair(nv); 
                // Looks like a MITM
                return false;
            }

            // Ensure the server challenge matched what we expected (aka the PIN was correct)
            byte[] serverChallengeRespHash = ToSHA1Bytes(concatBytes(concatBytes(randomChallenge, serverCert.GetSignature()), serverSecret));
            if (!serverChallengeRespHash.SequenceEqual(serverResponse))
            {
                // Cancel the pairing process
                await Unpair(nv); 
                // Probably got the wrong PIN
                return false;
            }

            // Send the server our signed secret
            byte[] clientPairingSecret = concatBytes(clientSecret, SignData(await provider.GetKeyPair(), clientSecret));
            XmlQuery clientSecretResp = new XmlQuery(nv.BaseUrl +
                    "/pair?uniqueid=" + uniqueId + "&devicename=roth&updateState=1&clientpairingsecret=" + BytesToHex(clientPairingSecret));
            result = await clientSecretResp.ReadXmlElement("paired");
            if (result == null || !result.Equals("1"))
            {
                await Unpair(nv);
                return false; 
            }

            // Do the initial challenge (seems neccessary for us to show as paired)
            XmlQuery pairChallenge = new XmlQuery(nv.BaseUrl + "/pair?uniqueid=" + uniqueId + "&devicename=roth&updateState=1&phrase=pairchallenge");

            result = await pairChallenge.ReadXmlElement("paired");
            if (result == null || !result.Equals("1"))
            {
                await Unpair(nv);
                return false; 
            }
            return true; 
        } 
Ejemplo n.º 10
0
        /// <summary>
        /// Quit Game Event Handler
        /// </summary>
        private async Task QuitGame_Common()
        {
            Computer selected = (Computer)computerPicker.SelectedItem;

            // User hasn't selected anything or selected the placeholder
            if (selected == null || selected.IpAddress == null)
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "No machine selected", "Quit Failed");
                return;
            }

            PairingManager p = new PairingManager(selected);
            if (await p.QueryPairState() != true)
            {
                DialogUtils.DisplayDialog(this.Dispatcher, "Device not paired", "Quit Failed");
                return;
            }

            try
            {
                NvHttp nv = new NvHttp(selected.IpAddress);
                XmlQuery quit = new XmlQuery(nv.BaseUrl + "/cancel?uniqueid=" + nv.GetUniqueId());
                string cancelled = await quit.ReadXmlElement("cancel");
                if (cancelled == "1")
                {
                    DialogUtils.DisplayDialog(this.Dispatcher, "Successfully Quit Game", "Quit Game");
                    return;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }

            DialogUtils.DisplayDialog(this.Dispatcher, "Unable to quit", "Quit Game");
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Starts the connection by calling into Moonlight Common
        /// </summary>
        private async Task StartConnection(MoonlightStreamConfiguration streamConfig)
        {
            NvHttp nv = null;
            await SetStateText("Resolving hostname...");
            try
            {
                nv = new NvHttp(context.computer.IpAddress);
            }
            catch (ArgumentNullException)
            {
                stageFailureText = "Error resolving hostname";
                ConnectionFailed();
                return;
            }

            String serverIp = null;
            try
            {
                serverIp = await nv.ResolveServerIPAddress();
            }
            catch (Exception)
            {
                stageFailureText = "Error resolving hostname";
                ConnectionFailed();
                return;
            }

            // Set up callbacks
            MoonlightDecoderRenderer drCallbacks = new MoonlightDecoderRenderer(DrSetup, DrCleanup, DrSubmitDecodeUnit);
            MoonlightAudioRenderer arCallbacks = new MoonlightAudioRenderer(ArInit, ArCleanup, ArPlaySample);
            MoonlightConnectionListener clCallbacks = new MoonlightConnectionListener(ClStageStarting, ClStageComplete, ClStageFailed,
            ClConnectionStarted, ClConnectionTerminated, ClDisplayMessage, ClDisplayTransientMessage);

            // Launch Steam
            await SetStateText("Launching Steam");
            if (await StartOrResumeApp(nv, streamConfig) == false)
            {
                Debug.WriteLine("Can't find app");
                stageFailureText = "Error launching App";
                ConnectionFailed();
                return;
            }

            // Call into Common to start the connection
            Debug.WriteLine("Starting connection");

            MoonlightCommonRuntimeComponent.StartConnection(serverIp, streamConfig, clCallbacks, drCallbacks, arCallbacks, serverMajorVersion);

            if (stageFailureText != null)
            {
                Debug.WriteLine("Stage failed");
                ConnectionFailed();
                return;
            }
            else
            {
                ConnectionSuccess();
            }
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Starts the connection by calling into Moonlight Common
        /// </summary>
        private async Task StartConnection(MoonlightStreamConfiguration streamConfig)
        {
            NvHttp nv = null;

            await SetStateText("Resolving hostname...");

            try
            {
                nv = new NvHttp(context.computer.IpAddress);
            }
            catch (ArgumentNullException)
            {
                stageFailureText = "Error resolving hostname";
                ConnectionFailed();
                return;
            }

            String serverIp = null;

            try
            {
                serverIp = await nv.ResolveServerIPAddress();
            }
            catch (Exception)
            {
                stageFailureText = "Error resolving hostname";
                ConnectionFailed();
                return;
            }

            // Set up callbacks
            MoonlightDecoderRenderer    drCallbacks = new MoonlightDecoderRenderer(DrSetup, DrCleanup, DrSubmitDecodeUnit);
            MoonlightAudioRenderer      arCallbacks = new MoonlightAudioRenderer(ArInit, ArCleanup, ArPlaySample);
            MoonlightConnectionListener clCallbacks = new MoonlightConnectionListener(ClStageStarting, ClStageComplete, ClStageFailed,
                                                                                      ClConnectionStarted, ClConnectionTerminated, ClDisplayMessage, ClDisplayTransientMessage);

            // Launch Steam
            await SetStateText("Launching Steam");

            if (await StartOrResumeApp(nv, streamConfig) == false)
            {
                Debug.WriteLine("Can't find app");
                stageFailureText = "Error launching App";
                ConnectionFailed();
                return;
            }

            // Call into Common to start the connection
            Debug.WriteLine("Starting connection");

            MoonlightCommonRuntimeComponent.StartConnection(serverIp, streamConfig, clCallbacks, drCallbacks, arCallbacks, serverMajorVersion);

            if (stageFailureText != null)
            {
                Debug.WriteLine("Stage failed");
                ConnectionFailed();
                return;
            }
            else
            {
                ConnectionSuccess();
            }
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Create start HTTP request
        /// </summary>
        private async Task <bool> StartOrResumeApp(NvHttp nv, MoonlightStreamConfiguration streamConfig)
        {
            XmlQuery serverInfo        = new XmlQuery(nv.BaseUrl + "/serverinfo?uniqueid=" + nv.GetUniqueId());
            string   currentGameString = await serverInfo.ReadXmlElement("currentgame");

            if (currentGameString == null)
            {
                return(false);
            }

            string versionString = await serverInfo.ReadXmlElement("appversion");

            if (versionString == null)
            {
                return(false);
            }

            serverMajorVersion = Convert.ToInt32(versionString.Substring(0, 1));

            byte[] aesIv   = streamConfig.GetRiAesIv();
            int    riKeyId =
                (int)(((aesIv[0] << 24) & 0xFF000000U) |
                      ((aesIv[1] << 16) & 0xFF0000U) |
                      ((aesIv[2] << 8) & 0xFF00U) |
                      (aesIv[3] & 0xFFU));
            string riConfigString =
                "&rikey=" + PairingCryptoHelpers.BytesToHex(streamConfig.GetRiAesKey()) +
                "&rikeyid=" + riKeyId;

            // Launch a new game if nothing is running
            if (currentGameString == null || currentGameString.Equals("0"))
            {
                XmlQuery x = new XmlQuery(nv.BaseUrl + "/launch?uniqueid=" + nv.GetUniqueId() + "&appid=" + context.appId +
                                          "&mode=" + streamConfig.GetWidth() + "x" + streamConfig.GetHeight() + "x" + streamConfig.GetFps() +
                                          "&additionalStates=1&sops=1" + // FIXME: make sops configurable
                                          riConfigString);

                string sessionStr = await x.ReadXmlElement("gamesession");

                if (sessionStr == null || sessionStr.Equals("0"))
                {
                    return(false);
                }

                return(true);
            }
            else
            {
                // A game was already running, so resume it
                // FIXME: Quit and relaunch if it's not the game we came to start
                XmlQuery x = new XmlQuery(nv.BaseUrl + "/resume?uniqueid=" + nv.GetUniqueId() + riConfigString);

                string resumeStr = await x.ReadXmlElement("resume");

                if (resumeStr == null || resumeStr.Equals("0"))
                {
                    return(false);
                }

                return(true);
            }
        }
Ejemplo n.º 14
0
 /// <summary>
 /// Constructor that sets nv
 /// </summary>
 /// <param name="nv">The NvHttp Object</param>
 public PairingManager(Computer computer)
 {
     this.nv = new NvHttp(computer.IpAddress);
 }