private RemotePlayContext SendInitControlMessages(Socket udpClient) { /******** Initial control message 1 ********/ byte[] controlMessage1Payload = HexUtil.Unhexlify("0064006400004823"); ushort initialControlMessage1PayloadSize = (ushort)(12 + controlMessage1Payload.Length); ControlMessage initialMessage1 = new ControlMessage(0, 0, 0, 0, 1, 0, initialControlMessage1PayloadSize, 0x4823, 0x19000); initialMessage1.UnParsedPayload = controlMessage1Payload; byte[] initialControlMessage1Data = GetByteArrayForControlMessage(initialMessage1); ControlResult initControlResult1 = SendControlDataAndWaitForAnswer(udpClient, initialControlMessage1Data, 1, "Send Init Control message 1"); if (!initControlResult1.WasSuccessful) { return(null); } ControlMessage initialAnswer1 = initControlResult1.ControlMessages[0]; /******** Initial control message 2 ********/ byte[] initialAnswer1Payload = initialAnswer1.UnParsedPayload; MemoryStream memoryBuffer = new MemoryStream(initialAnswer1Payload) { Position = 8 }; byte[] funcIncrBuffer = new byte[4]; memoryBuffer.Read(funcIncrBuffer, 0, funcIncrBuffer.Length); uint funcIncrValue = ByteUtil.ByteArrayToUInt(funcIncrBuffer); memoryBuffer.Position = 28; byte[] lastAnswerPart = new byte[memoryBuffer.Length - memoryBuffer.Position]; memoryBuffer.Read(lastAnswerPart, 0, lastAnswerPart.Length); byte[] funcIncr = ByteUtil.UIntToByteArray(initialAnswer1.FuncIncr); byte[] classValue = ByteUtil.UIntToByteArray(initialAnswer1.ClassValue); byte[] controlMessage2Payload = ByteUtil.ConcatenateArrays(funcIncr, classValue, funcIncr, lastAnswerPart); ushort initialControlMessage2PayloadSize = (ushort)(12 + controlMessage2Payload.Length); ControlMessage controlMessage2 = new ControlMessage(0, initialAnswer1.FuncIncr, 0, 0, 10, 0, initialControlMessage2PayloadSize, funcIncrValue, initialAnswer1.ReceiverId); controlMessage2.UnParsedPayload = controlMessage2Payload; byte[] initialControlMessage2Data = GetByteArrayForControlMessage(controlMessage2); ControlResult initControlResult2 = SendControlDataAndWaitForAnswer(udpClient, initialControlMessage2Data, 1, "Send Init Control message 2"); if (!initControlResult2.WasSuccessful) { return(null); } RemotePlayContext remotePlayContext = new RemotePlayContext() { ReceiverId = initialAnswer1.FuncIncr, FuncIncr = initialMessage1.FuncIncr }; return(remotePlayContext); }
/*********** heartbeat handling ***********/ private void HandleHeartBeat(Socket udpSocket, RemotePlayContext remotePlayContext) { Thread heartBeatThread = new Thread(() => { try { do { Thread.Sleep(1000); byte[] heartbeatPayload = HexUtil.Unhexlify("000803"); ushort heartbeatPayloadSize = (ushort)(12 + heartbeatPayload.Length); ControlMessage heartbeat = new ControlMessage((byte)0, remotePlayContext.ReceiverId, 0, 0, 0, 1, heartbeatPayloadSize, remotePlayContext.FuncIncr, 0x10000); heartbeat.UnParsedPayload = heartbeatPayload; SendCryptedControlMessage(heartbeat, remotePlayContext, udpSocket); } while (true); } catch (Exception) { // ignore } }); heartBeatThread.IsBackground = true; heartBeatThread.Start(); }
private void HandleFeedbackData(Socket udpSocket, RemotePlayContext remotePlayContext) { Thread heartBeatThread = new Thread(() => { try { do { FeedbackMessage feedbackMessage = new FeedbackMessage(_feedbackSequenceNumber, 0, 0, 0); byte[] buf = new byte[25]; buf[0x0] = 0xa0; // TODO buf[0x1] = 0xff; // TODO buf[0x2] = 0x7f; // TODO buf[0x3] = 0xff; // TODO buf[0x4] = 0x7f; // TODO buf[0x5] = 0xff; // TODO buf[0x6] = 0x7f; // TODO buf[0x7] = 0xff; // TODO buf[0x8] = 0x7f; // TODO buf[0x9] = 0x99; // TODO buf[0xa] = 0x99; // TODO buf[0xb] = 0xff; // TODO buf[0xc] = 0x7f; // TODO buf[0xd] = 0xfe; // TODO buf[0xe] = 0xf7; // TODO buf[0xf] = 0xef; // TODO buf[0x10] = 0x1f; // TODO buf[0x11] = 0; // TODO buf[0x12] = 0; // TODO buf[0x13] = 0; // TODO buf[0x14] = 0; // TODO buf[0x15] = 0; // TODO buf[0x16] = 0; // TODO buf[0x17] = 0; // TODO buf[0x18] = 0; // TODO SendCryptedFeedbackMessage(feedbackMessage, buf, remotePlayContext, udpSocket); ++_feedbackSequenceNumber; Thread.Sleep(200); } while (true); } catch (Exception) { // ignore } }); heartBeatThread.IsBackground = true; heartBeatThread.Start(); }
/*********** UDP request ***********/ public void InitializeRemotePlayChannel(Session session, IPEndPoint ps4Endpoint) { const int retry = 3; for (int i = 0; i < retry; i++) { Socket udpClient = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); udpClient.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); udpClient.ExclusiveAddressUse = false; udpClient.ReceiveTimeout = 5500; udpClient.Connect(ps4Endpoint.Address, RemotePlayPort); RemotePlayContext remotePlayContext = SendInitControlMessages(udpClient); if (remotePlayContext == null) { udpClient.Close(); udpClient.Dispose(); Thread.Sleep(2500); continue; } remotePlayContext = SendBigBangMessages(udpClient, session, remotePlayContext); if (remotePlayContext == null) { udpClient.Close(); udpClient.Dispose(); Thread.Sleep(2500); continue; } remotePlayContext = SendStreamInfo(udpClient, session, remotePlayContext); if (remotePlayContext == null) { udpClient.Close(); udpClient.Dispose(); Thread.Sleep(2500); continue; } OnPs4LogInfo?.Invoke(this, "!!!!!!!!!!!!! Stream initialization successfully completed" + Environment.NewLine); _udpClient = udpClient; RemotePlayAsyncState remotePlayAsyncState = new RemotePlayAsyncState(remotePlayContext, session); //HandleFeedbackData(_udpClient, remotePlayContext); HandleHeartBeat(_udpClient, remotePlayContext); _udpClient.BeginReceive(remotePlayAsyncState.Buffer, 0, remotePlayAsyncState.Buffer.Length, SocketFlags.None, HandleRemotePlayStream, remotePlayAsyncState); break; } }
private void SendCryptedControlMessage(ControlMessage controlMessage, RemotePlayContext remotePlayContext, Socket udpClient) { controlMessage.TagPos = 0; byte[] controlMessageData = GetByteArrayForControlMessage(controlMessage); int macOffset = GetMacOffset(0); // crypto position CryptoService.AddGmacToBuffer(remotePlayContext.LocalGmacInfo, remotePlayContext.LocalKeyPosition, controlMessageData, macOffset); byte[] keyPosBuffer = ByteUtil.UIntToByteArray(remotePlayContext.LocalKeyPosition); int position = GetTagPosOffset(0); for (int i = 0; i < keyPosBuffer.Length; i++) { controlMessageData[position + i] = keyPosBuffer[i]; } remotePlayContext.LocalKeyPosition += 16; //controlMessage.UnParsedPayload.Length < 16 ? 16 : controlMessage.UnParsedPayload.Length; SendData(udpClient, controlMessageData); }
private ControlResult SendCryptedControlMessageAndWaitForAnswer(ControlMessage controlMessage, RemotePlayContext remotePlayContext, Socket udpClient, int expectedPackets, string info) { controlMessage.TagPos = 0; byte[] controlMessageData = GetByteArrayForControlMessage(controlMessage); int macOffset = GetMacOffset(0); // crypto position CryptoService.AddGmacToBuffer(remotePlayContext.LocalGmacInfo, remotePlayContext.LocalKeyPosition, controlMessageData, macOffset); byte[] keyPosBuffer = ByteUtil.UIntToByteArray(remotePlayContext.LocalKeyPosition); int position = GetTagPosOffset(0); for (int i = 0; i < keyPosBuffer.Length; i++) { controlMessageData[position + i] = keyPosBuffer[i]; } remotePlayContext.LocalKeyPosition += 16; // controlMessage.UnParsedPayload.Length < 16 ? 16 : controlMessage.UnParsedPayload.Length; return(SendControlDataAndWaitForAnswer(udpClient, controlMessageData, expectedPackets, info)); }
private void SendCryptedFeedbackMessage(FeedbackMessage feedbackMessage, byte[] feebdackBuffer, RemotePlayContext remotePlayContext, Socket udpClient) { feedbackMessage.TagPos = (uint)remotePlayContext.LocalKeyPosition; byte[] feedbackMessageData = GetByteArrayForFeedbackMessage(feedbackMessage); int macOffset = GetMacOffset(6); // crypto position CryptoService.AddGmacToBuffer(remotePlayContext.LocalGmacInfo, feedbackMessage.TagPos, feedbackMessageData, macOffset); feebdackBuffer = CryptoService.EncryptGmacMessage(remotePlayContext.LocalGmacInfo, feedbackMessage.TagPos, feebdackBuffer); remotePlayContext.LocalKeyPosition += 16; //controlMessage.UnParsedPayload.Length < 16 ? 16 : controlMessage.UnParsedPayload.Length; SendData(udpClient, ByteUtil.ConcatenateArrays(feedbackMessageData, feebdackBuffer)); }
private RemotePlayContext SendStreamInfo(Socket udpClient, Session session, RemotePlayContext remotePlayContext) { /******** Stream info receive ********/ ControlResult resolutionInfoPayload = WaitForControlMessage(udpClient, 1, "Wait for stream info payload").Result; if (!resolutionInfoPayload.WasSuccessful) { if (remotePlayContext.LastSentMessage.Length > 0) { resolutionInfoPayload = SendControlDataAndWaitForAnswer(udpClient, remotePlayContext.LastSentMessage, 1, "Resend bang ack control message and wait for stream info payload"); if (!resolutionInfoPayload.WasSuccessful) { return(null); } } else { return(null); } } ControlMessage resolutionInfoControlMessage = resolutionInfoPayload.ControlMessages[0]; TakionMessage resolutionPayload = Serializer.Deserialize <TakionMessage>(new MemoryStream(resolutionInfoControlMessage.UnParsedPayload)); if (resolutionPayload.streamInfoPayload == null) { return(null); } remotePlayContext.StreamInfoPayload = resolutionPayload.streamInfoPayload; byte[] ackResolutionInfoPayload = HexUtil.Unhexlify("00000000"); ushort ackResolutionInfoSize = (ushort)(12 + ackResolutionInfoPayload.Length); ControlMessage ackResolutionInfoControlMessage = new ControlMessage((byte)0, remotePlayContext.ReceiverId, 0, 0, 3, 0, ackResolutionInfoSize, resolutionInfoControlMessage.FuncIncr, 0x19000); ackResolutionInfoControlMessage.UnParsedPayload = ackResolutionInfoPayload; byte[] ackResolutionInfoControlMessageData = GetByteArrayForControlMessage(ackResolutionInfoControlMessage); remotePlayContext.LastSentMessage = ackResolutionInfoControlMessageData; SendCryptedControlMessage(ackResolutionInfoControlMessage, remotePlayContext, udpClient); // ToDo handle when there is no further response and and resolution info is sent again //SendData(udpClient, ackResolutionInfoControlMessageData); /******** Stream info ack send ********/ remotePlayContext.LastSentMessage = new byte[0]; byte[] streamInfoAckPayload = HexUtil.Unhexlify("00080e"); ushort streamInfoAckPayloadSize = (ushort)(12 + streamInfoAckPayload.Length); ControlMessage streamInfoAckControlMessage = new ControlMessage((byte)0, remotePlayContext.ReceiverId, 0, 0, 0, 1, streamInfoAckPayloadSize, remotePlayContext.FuncIncr, 0x90000); streamInfoAckControlMessage.UnParsedPayload = streamInfoAckPayload; ControlResult controlResult = SendCryptedControlMessageAndWaitForAnswer(streamInfoAckControlMessage, remotePlayContext, udpClient, 1, "Sending stream info ack message" + Environment.NewLine); if (!controlResult.WasSuccessful) { const int retry = 0; for (int i = 0; i < retry; i++) { SendData(udpClient, remotePlayContext.LastSentMessage); controlResult = SendCryptedControlMessageAndWaitForAnswer(streamInfoAckControlMessage, remotePlayContext, udpClient, 1, "Resend stream info ack message" + Environment.NewLine); if (controlResult.WasSuccessful) { break; } } } if (!controlResult.WasSuccessful) { return(null); } return(remotePlayContext); }
private RemotePlayContext SendBigBangMessages(Socket udpClient, Session session, RemotePlayContext remotePlayContext) { /******** Big Payload send ********/ // Generate random handshake key, for ECDH pubkey signature calculation byte[] handshakeKey = new byte[16]; new Random().NextBytes(handshakeKey); // Generate ECDH keypair var ecdhKeyPair = CryptoService.GenerateEcdhKeyPair(); // Get public key bytes var ownPublicKey = Session.GetPublicKeyBytesFromKeyPair(ecdhKeyPair); // Calculate ECDH pubkey signature var ecdhSignature = Session.CalculateHMAC(handshakeKey, ownPublicKey); int unixTimestamp = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; string timestampUnix = unixTimestamp.ToString(); string sessionKey = timestampUnix + CryptoService.GetUniqueKey(64); LaunchSpecification launchSpecs = LaunchSpecification.GetStandardSpecs("sessionId123", handshakeKey); byte[] launchSpecBuffer = Encoding.UTF8.GetBytes(launchSpecs.Serialize()); byte[] cryptoBuffer = new byte[launchSpecBuffer.Length]; cryptoBuffer = session.Encrypt(cryptoBuffer, 0); byte[] newLaunchSpec = new byte[launchSpecBuffer.Length]; for (int i = 0; i < launchSpecBuffer.Length; i++) { newLaunchSpec[i] = (byte)(launchSpecBuffer[i] ^ cryptoBuffer[i]); } TakionMessage takionBigPayloadMessage = new TakionMessage { Type = TakionMessage.PayloadType.Big, bigPayload = new BigPayload { clientVersion = 9, sessionKey = sessionKey, launchSpec = Convert.ToBase64String(newLaunchSpec), encryptedKey = new byte[] { 0, 0, 0, 0 }, ecdhPubKey = ownPublicKey, ecdhSig = ecdhSignature } }; MemoryStream bigPayloadStream = new MemoryStream(); Serializer.Serialize(bigPayloadStream, takionBigPayloadMessage); byte[] bigPayloadBuffer = ByteUtil.ConcatenateArrays(new byte[1], bigPayloadStream.ToArray()); // Padding byte + BigPayload ushort bigPayloadSize = (ushort)(12 + bigPayloadBuffer.Length); ControlMessage controlMessageBigPayload = new ControlMessage(0, remotePlayContext.ReceiverId, 0, 0, 0, 1, bigPayloadSize, remotePlayContext.FuncIncr, 0x10000); controlMessageBigPayload.UnParsedPayload = bigPayloadBuffer; byte[] initialControlMessage2Data = GetByteArrayForControlMessage(controlMessageBigPayload); OnPs4LogInfo?.Invoke(this, Environment.NewLine + "Sending big payload:"); OnPs4LogInfo?.Invoke(this, "ECDH pubkey: " + HexUtil.Hexlify(takionBigPayloadMessage.bigPayload.ecdhPubKey)); OnPs4LogInfo?.Invoke(this, "ECDH sig: " + HexUtil.Hexlify(takionBigPayloadMessage.bigPayload.ecdhSig)); OnPs4LogInfo?.Invoke(this, "Session key: " + takionBigPayloadMessage.bigPayload.sessionKey + Environment.NewLine); ControlResult bigPayloadResult = SendControlDataAndWaitForAnswer(udpClient, initialControlMessage2Data, 2, "Send BigPayload"); if (!bigPayloadResult.WasSuccessful) { return(null); } /******** Bang Payload receive ********/ ControlMessage answerPacket1 = bigPayloadResult.ControlMessages[0]; ControlMessage answerPacket2 = bigPayloadResult.ControlMessages[1]; if (answerPacket1.ProtoBuffFlag != 1 && answerPacket2.ProtoBuffFlag != 1) { return(null); } TakionMessage bangPayload = answerPacket1.ProtoBuffFlag == 1 ? Serializer.Deserialize <TakionMessage>(new MemoryStream(answerPacket1.UnParsedPayload)) : Serializer.Deserialize <TakionMessage>(new MemoryStream(answerPacket2.UnParsedPayload)); if (bangPayload.bangPayload == null) { return(null); } ControlMessage bangPayloadControl = answerPacket1.ProtoBuffFlag == 1 ? answerPacket1 : answerPacket2; OnPs4LogInfo?.Invoke(this, Environment.NewLine + "Received bang payload:"); OnPs4LogInfo?.Invoke(this, "ECDH pubkey: " + HexUtil.Hexlify(bangPayload.bangPayload.ecdhPubKey)); OnPs4LogInfo?.Invoke(this, "ECDH sig: " + HexUtil.Hexlify(bangPayload.bangPayload.ecdhSig)); OnPs4LogInfo?.Invoke(this, "Session key: " + bangPayload.bangPayload.sessionKey); /* Derive ECDH shared secret */ var foreignPubkeyParams = Session.ConvertPubkeyBytesToCipherParams(bangPayload.bangPayload.ecdhPubKey); remotePlayContext.SharedSecret = Session.GenerateSharedSecret(ecdhKeyPair.Private, foreignPubkeyParams); remotePlayContext.LocalGmacInfo = CryptoService.SetUpGmac(2, handshakeKey, remotePlayContext.SharedSecret); remotePlayContext.RemoteGmacInfo = CryptoService.SetUpGmac(3, handshakeKey, remotePlayContext.SharedSecret); OnPs4LogInfo?.Invoke(this, "HANDSHAKE KEY: " + HexUtil.Hexlify(handshakeKey)); OnPs4LogInfo?.Invoke(this, "SHARED SECRET: " + HexUtil.Hexlify(remotePlayContext.SharedSecret)); byte[] ackBangPayload = HexUtil.Unhexlify("00000000"); ushort ackBangPayloadSize = (ushort)(12 + ackBangPayload.Length); ControlMessage ackBangPayloadMessage = new ControlMessage(0, bangPayloadControl.FuncIncr, 0, 0, 3, 0, ackBangPayloadSize, bangPayloadControl.FuncIncr, 0x19000); ackBangPayloadMessage.UnParsedPayload = ackBangPayload; byte[] ackBangPayloadMessageData = GetByteArrayForControlMessage(ackBangPayloadMessage); remotePlayContext.LastSentMessage = ackBangPayloadMessageData; SendData(udpClient, ackBangPayloadMessageData); return(remotePlayContext); }
public RemotePlayAsyncState(RemotePlayContext remotePlayContext, Session session) { this.RemotePlayContext = remotePlayContext; this.Session = session; }