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); }
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 void HandleControlMessage(byte[] payload, Session session) { if (payload[0] == 0) // Control Packet { byte[] message = payload; ControlMessage controlMessage = new ControlMessage(); using (MemoryStream memoryStream = new MemoryStream(message, 0, message.Length)) using (BinaryReader binaryWriter = new BinaryReader(memoryStream)) { controlMessage.Deserialize(binaryWriter); } if (controlMessage.ProtoBuffFlag == 1 && controlMessage.PLoadSize > 100) { TakionMessage takionMessage = ProtobufUtil.Deserialize <TakionMessage>(controlMessage.UnParsedPayload); if (takionMessage.bigPayload != null) { this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload session key: " + takionMessage.bigPayload.sessionKey))); this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload ecdh pub key in hex: " + HexUtil.Hexlify(takionMessage.bigPayload.ecdhPubKey)))); this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload ecdh sig in hex: " + HexUtil.Hexlify(takionMessage.bigPayload.ecdhSig)))); if (session != null) { byte[] launchSpecBuffer = Convert.FromBase64String(takionMessage.bigPayload.launchSpec); byte[] cryptoBuffer = new byte[launchSpecBuffer.Length]; cryptoBuffer = session.Encrypt(cryptoBuffer, 0); byte[] newLaunchSpec = new byte[launchSpecBuffer.Length]; for (int j = 0; j < launchSpecBuffer.Length; j++) { newLaunchSpec[j] = (byte)(launchSpecBuffer[j] ^ cryptoBuffer[j]); } string launchSpecs = Encoding.UTF8.GetString(newLaunchSpec); LaunchSpecification launchSpecJsonObject = LaunchSpecification.Deserialize(launchSpecs); byte[] handshakeKey = launchSpecJsonObject.HandshakeKey; if (handshakeKey != null) { this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload handshake key in launchSpec in hex: " + HexUtil.Hexlify(handshakeKey)))); var ecdhSignatureVerification = Session.CalculateHMAC(handshakeKey, takionMessage.bigPayload.ecdhPubKey); if (ecdhSignatureVerification.SequenceEqual(takionMessage.bigPayload.ecdhSig)) { this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("ECDH Signature matches!"))); } else { this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!!! ECDH Signature mismatch"))); } } this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Big payload full launchSpec: " + Environment.NewLine + launchSpecJsonObject.Serialize() + Environment.NewLine))); } else { this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox(Environment.NewLine))); } } else if (takionMessage.bangPayload != null) { this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Bang payload session key: " + takionMessage.bangPayload.sessionKey))); this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Bang payload ecdh pub key in hex: " + HexUtil.Hexlify(takionMessage.bangPayload.ecdhPubKey)))); this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Bang payload ecdh sig in hex: " + HexUtil.Hexlify(takionMessage.bangPayload.ecdhSig) + Environment.NewLine))); } } else { if (controlMessage.Crypto != 0) { this.textBoxPcapLogOutput.Invoke(new MethodInvoker(() => AppendLogOutputToPcapLogTextBox("!!! Control message with crypto value: " + HexUtil.Hexlify(ByteUtil.UIntToByteArray(controlMessage.Crypto))))); } } } }