RPC XOR encryption

Hello, recently my game players began to intercept the data and change them. For protection, I added xor encryption. I wanted to see this method in future updates Photon.
Protection was written by students who hack the game for instructions

NetworkingPeer.cs => RPC

        //ts: changed RPCs to a one-level hashtable as described in internal.txt
        Hashtable rpcEvent = new Hashtable();

	int encryptKey = UnityEngine.Random.Range(1,255);
	rpcEvent[(byte)6] = (byte)encryptKey;

	rpcEvent[(byte)0] = (int)view.viewID ^ encryptKey; // LIMITS NETWORKVIEWS&PLAYERS
        if (view.prefix > 0)
        {
			rpcEvent[(byte)1] = (short)view.prefix ^ (short)encryptKey;
        }
        rpcEvent[(byte)2] = PhotonNetwork.ServerTimestamp ^ encryptKey;


        // send name or shortcut (if available)
        int shortcut = 0;
        if (rpcShortcuts.TryGetValue(methodName, out shortcut))
        {
            rpcEvent[(byte)5] = (byte)shortcut ^ (byte)encryptKey; // LIMITS RPC COUNT
        }
        else
        {
			string encryptMethodName = "";
			for(int i=0;i<methodName.Length;i++){
				encryptMethodName += (char)(methodName[i] ^ (char)encryptKey);
			}
			rpcEvent[(byte)3] = encryptMethodName;
        }


NetworkingPeer.cs => ExecuteRpc
        // ts: updated with "flat" event data
	int encryptKey = (int)((byte)rpcData[(byte)6]);
        int netViewID = (int)rpcData[(byte)0] ^ encryptKey; // LIMITS PHOTONVIEWS&PLAYERS
        int otherSidePrefix = 0;    // by default, the prefix is 0 (and this is not being sent)
        if (rpcData.ContainsKey((byte)1))
        {
            otherSidePrefix = (short)rpcData[(byte)1] ^ (short)encryptKey;
        }


        string inMethodName;
        if (rpcData.ContainsKey((byte)5))
        {
            int rpcIndex = (int)rpcData[(byte)5] ^ encryptKey;  // LIMITS RPC COUNT
            if (rpcIndex > PhotonNetwork.PhotonServerSettings.RpcList.Count - 1)
            {
                Debug.LogError("Could not find RPC with index: " + rpcIndex + ". Going to ignore! Check PhotonServerSettings.RpcList");
                return;
            }
            else
            {
                inMethodName = PhotonNetwork.PhotonServerSettings.RpcList[rpcIndex];
            }
        }
        else
        {
		string encryptMethodName = (string)rpcData[(byte)3];
		inMethodName = "";
		for(int i=0;i<encryptMethodName.Length;i++){
			inMethodName += (char)(encryptMethodName[i] ^ (char)encryptKey);
		}
        }

Comments

  • Out of curiosity: Did they achieve something useful by modifying the data on the network?
    Encryption is optional in Photon (and too expensive to turn on by default) and we also have an option to include a CRC value for the messages (which help detect modified values).
    Your solution is also interesting. Thanks for posting.
  • Tobias said:

    Out of curiosity: Did they achieve something useful by modifying the data on the network?

    Some learned that rpcIndex = 12 we have in the game is responsible for the damage and ignored it
  • I spent a performance test
    Count = 1000
    Original: 1ms
    Encrypt: 1ms
    
    Count = 10000
    Original: 24ms
    Encrypt: 26ms
    
    Count = 100000
    Original: 208ms
    Encrypt: 247ms
    
    	public int count = 10000;
    	public bool isEncrypt;
    
    	void Update(){
    		if(Input.GetKeyDown(KeyCode.F)){
    			if(isEncrypt){
    				TestEncrypt();
    			}else{
    				TestOriginal();
    			}
    		}
    	}
    
    
    
    	void TestEncrypt(){
    		Stopwatch watch = Stopwatch.StartNew();
    		watch.Start();
    		for(int i=0;i<count;i++){
    			Hashtable rpcEvent = new Hashtable();
    			int encryptKey = UnityEngine.Random.Range(1,255);
    			rpcEvent[(byte)6] = (byte)encryptKey;
    			rpcEvent[(byte)0] = 2 ^ encryptKey; // LIMITS NETWORKVIEWS&PLAYERS
    			rpcEvent[(byte)1] = (short)1 ^ (short)encryptKey;
    			rpcEvent[(byte)2] = 5 ^ encryptKey;
    			rpcEvent[(byte)5] = (byte)32 ^ (byte)encryptKey; // LIMITS RPC COUNT
    		}
    		watch.Stop();
    		print("Encrypt: " + watch.ElapsedMilliseconds + " ms");
    	}
    
    	void TestOriginal(){
    		Stopwatch watch = Stopwatch.StartNew();
    		watch.Start();
    		for(int i=0;i<count;i++){
    			Hashtable rpcEvent = new Hashtable();
    			rpcEvent[(byte)0] = 2; // LIMITS NETWORKVIEWS&PLAYERS
    			rpcEvent[(byte)1] = (short)1;
    			rpcEvent[(byte)2] = 5;
    			rpcEvent[(byte)5] = (byte)32; // LIMITS RPC COUNT
    		}
    		watch.Stop();
    		print("Original: " + watch.ElapsedMilliseconds + " ms");
    	}
  • Tobias said:

    (and too expensive to turn on by default)

    It will not be expensive, as the encryption method I use in my CustomTypes in Photon
  • Hmm, yes, this is cheap.
    Do you also somehow prevent in-memory cheating? Or are your players only modifying network data currently?
  • Tobias said:

    Do you also somehow prevent in-memory cheating?

    Yes, I use the Anti-Cheat Toolkit. It also encrypts data using XOR.