So before we create our login, we need to take the time to handle encryption. There are several file modifications we must perform to pass along when we want to encrypt an operation. We begin with IGameState. The last function is SendOperation, this now changes to:
void SendOperation(Game gameLogic, OperationCode operationCode, Hashtable parameter, bool sendReliable, byte channelId, bool encrypt);
Because of this change we have to modify Disconnected, WaitingForConnect, and Connected. Modify each of those classes so they also have bool encrypt at the end. Lastly we need to modify the code inside Connected.SendOperation to be the following:
public void SendOperation(Game gameLogic, AegisBornCommon.OperationCode operationCode, System.Collections.Hashtable parameter, bool sendReliable, byte channelId, bool encrypt)
{
gameLogic.Peer.OpCustom((byte)operationCode, parameter, sendReliable, channelId, encrypt);
}
Disconnected and WaitingForConnect only need the first change because they have no code in send operation.
With this change in place we need to modify Game.SendOp to pass along an encrypt:
public void SetConnected()
{
_stateStrategy = Connected.Instance;
_listener.OnConnect(this);
_peer.OpExchangeKeysForEncryption();
}
public delegate void AfterKeysExchanged();
public AfterKeysExchanged afterKeysExchanged;
public void NotifyKeysExchanged()
{
if (afterKeysExchanged != null)
{
afterKeysExchanged();
}
_listener.LogInfo(this, "Keys successfully exchanged");
}
In Connected.cs we need to handle the response as it comes back from the server in OnOperationResponse:
var handled = false;
switch(operationCode)
{
case OperationCode.ExchangeKeysForEncryption:
gameLogic.Peer.DeriveSharedKey((byte[])returnValues[(byte)ParameterCode.ServerKey]);
gameLogic.NotifyKeysExchanged();
handled = true;
break;
}
if (!handled)
{
gameLogic.OnUnexpectedPhotonReturn(returnCode, operationCode, returnValues);
}
At this point you will notice we need to add a few things to AegisBornCommon. Lets do that now. In OperationCode.cs we need to add the ExchangeKeysForEncryption code:
///<summary>
/// Code for exchanging keys using PhotonPeer.OpExchangeKeysForEncryption
///</summary>
ExchangeKeysForEncryption = 95,
Then we need to add a couple values to the ParameterCode.cs file:
///<summary>
/// Client key parameter used to establish secure communication.
///</summary>
ClientKey = 16,
///<summary>
/// Server key parameter used to establish secure communication.
///</summary>
ServerKey = 17,
Once you recompile and ensure the new dll is in the Plugins directory in your Unity3d project the compile errors will disappear and you will be ready to implement the server portion of our key exchange.
First we need to create an operation to handle the exchange. I created a folder in AegisBorn called Operations and inside I created the EstablishSecureCommunicationOperation:
using AegisBornCommon;
using Photon.SocketServer;
using Photon.SocketServer.Rpc;
public class EstablishSecureCommunicationOperation : Operation
{
public EstablishSecureCommunicationOperation(OperationRequest operationRequest)
: base(operationRequest)
{
}
///<summary>
/// Gets or sets the clients public key.
///</summary>
[RequestParameter(Code = (short)ParameterCode.ClientKey, IsOptional = false)]
public byte[] ClientKey { get; set; }
///<summary>
/// Gets or sets the servers public key.
///</summary>
[ResponseParameter(Code = (short)ParameterCode.ServerKey, IsOptional = false)]
public byte[] ServerKey { get; set; }
}
This class' job is to take the information from the client, create a key, and send back a key to the client that the client will use when sending encrypted messages. Next we need to add our code to the dispatcher AegisBornPeer:
[Operation(OperationCode = (byte)OperationCode.ExchangeKeysForEncryption)]
public OperationResponse OperationKeyExchange(Peer peer, OperationRequest request)
{
var operation = new EstablishSecureCommunicationOperation(request);
if (operation.IsValid == false)
{
return new OperationResponse(request, (int)ErrorCode.InvalidOperationParameter, operation.GetErrorMessage());
}
// initialize the peer to support encrytion
operation.ServerKey = peer.PhotonPeer.InitializeEncryption(operation.ClientKey);
// publish the servers public key to the client
return operation.GetOperationResponse(0, "OK");
}
Thanks to the work we did earlier, this operation is automatically handled. It initializes the encryption and returns an OK response to the client. At this time I also went ahead and added the following code to the ErrorCode.cs file which comes directly from the Exit Games Demo:
///<summary>
/// The parameter out of range.
///</summary>
ParameterOutOfRange = 51,
///<summary>
/// The operation not supported.
///</summary>
OperationNotSupported,
///<summary>
/// The invalid operation parameter.
///</summary>
InvalidOperationParameter,
///<summary>
/// The invalid operation.
///
InvalidOperation,
With all this in place, when you click connect, you will get a message says Keys successfully exchanged. At this point we are ready to send an encrypted message to the server. You will notice that I used a switch statement in Connected.OnOperationResponse. We will be removing this and adding Operation Handlers in the near future. If you pull down the latest GitHub code, I already have them in place as well as what we will cover in the next blog, which is sending a login operation.
Comments
jonathan.deceptive
Wed, 07/06/2011 - 23:46
Permalink
Mismatches between this and old site
I'm sure I'm not the first going through the entire code here... but whilst working through this page I had trouble following what you were saying where code samples weren't matching up with the comments. I went back to your old site and there is a whole lot more code sections compared to here. Just a heads up to those who also were going through this and were confused. I think there must have been differences in other pages too, but I was able to work my head around those with out to much issue.
Thanks for a very informative run down of Photon.
dragagon
Thu, 07/07/2011 - 08:51
Permalink
Thanks for the comment here.
Thanks for the comment here. When moving from the other site, I reformatted some of the displays, in doing so I made a typo which caused this whole mess. It should look correct now.
xcalpro
Tue, 08/16/2011 - 10:01
Permalink
Missing some correction
I had been following along successfully up to this point, now I get all kinds of errors. First it was the _strategyState vs strategyState error which I corrected by finding the solution in the forum. Now I am getting errors for _listener, Peer, avatar, returnCode, and debug not existing in the current context.
It seems every time I fix one bug, 5 more pop up.
dragagon
Tue, 08/16/2011 - 12:12
Permalink
Ok, i need to go back through
Ok, i need to go back through a update a few places, i renamed private IGameListener listener to be _listener. And Peer should be private PhotonPeer _peer; avatar was deleted as it wasn't being used. still looking for debug and return code.
dragagon
Tue, 08/16/2011 - 12:38
Permalink
Ok found the rest of it, it
Ok found the rest of it, it was all because of part 10. The DebugReturn function should look like this:
public void DebugReturn(DebugLevel level, string message) { if (_listener.IsDebugLogEnabled) { _listener.LogDebug(this, message); } }Janidu
Fri, 10/14/2011 - 05:08
Permalink
Client Not Connecting
When I run Unity3d, it does not connect with Photon. The GUI gives the message "WaitingForConnect". I also get a Debug.Log message from the MMOEngine "LogDebug(Game game, string message)" method saying Connection refused.
Any idea what I'm doing wrong here
dragagon
Fri, 10/14/2011 - 07:41
Permalink
Normally the only time i get
Normally the only time i get a connection refused is when the Photon Instance isn't running or when you use the wrong Application Name or port in the server string. Try right clicking on the Photon control in the task bar and selecting open logs and see if one of the logs is telling you why it is refusing connection.
Janidu
Fri, 10/14/2011 - 08:10
Permalink
Thanks for the reply
Thanks for the reply. It was an issue with my IP addresses as I am running photon in a different machine.