/// <summary> /// Encodes the specified handshake message. /// </summary> /// <param name="message">The message.</param> /// <returns></returns> public byte[] Encode(ResonanceHandShakeMessage message) { using (MemoryStream ms = new MemoryStream()) { using (BinaryWriter writer = new BinaryWriter(ms)) { writer.Write((byte)0); //This empty byte is used to distinguish between handshake message and standard message. writer.Write((byte)message.Type); writer.Write(message.ClientId); writer.Write(message.RequireEncryption); if (message.RequireEncryption) { writer.Write(message.EncryptionPublicKey.ToStringOrEmpty()); writer.Write(message.SymmetricPassword.ToStringOrEmpty()); } } return(ms.ToArray()); } }
/// <summary> /// Begins the hand shake. /// This method will block execution until the handshake has completed. /// </summary> /// <exception cref="InvalidOperationException">Must call reset before attempting to begin a handshake.</exception> /// <exception cref="TimeoutException">Could not initiate a handshake within the given timeout.</exception> public void BeginHandShake() { if (!_wasReset) { throw new InvalidOperationException("Must call reset before attempting to begin a handshake."); } if (State == ResonanceHandShakeState.Idle) { Logger.LogDebug("Starting handshake..."); State = ResonanceHandShakeState.InProgress; Logger.LogDebug("Sending Handshake Request..."); ResonanceHandShakeMessage request = new ResonanceHandShakeMessage(); request.Type = ResonanceHandShakeMessageType.Request; request.ClientId = ClientID; request.RequireEncryption = EncryptionEnabled; request.EncryptionPublicKey = _publicKey; OnWriteHandShake(HandShakeTranscoder.Encode(request)); } bool cancel = false; TimeoutTask.StartNew(() => { cancel = true; }, TimeSpan.FromSeconds(10)); while (State != ResonanceHandShakeState.Completed && !cancel) { Thread.Sleep(10); } if (cancel) { throw new TimeoutException("Could not initiate a handshake within the given timeout."); } }
/// <summary> /// Decodes the raw data to a handshake message. /// </summary> /// <param name="data">The data.</param> /// <returns></returns> public ResonanceHandShakeMessage Decode(byte[] data) { using (MemoryStream ms = new MemoryStream(data)) { using (BinaryReader reader = new BinaryReader(ms)) { var message = new ResonanceHandShakeMessage(); reader.ReadByte(); //This empty byte is used to distinguish between handshake message and standard message. message.Type = (ResonanceHandShakeMessageType)reader.ReadByte(); message.ClientId = reader.ReadInt32(); message.RequireEncryption = reader.ReadBoolean(); if (message.RequireEncryption) { message.EncryptionPublicKey = reader.ReadString().ToNullIfEmpty(); message.SymmetricPassword = reader.ReadString().ToNullIfEmpty(); } return(message); } } }
/// <summary> /// To be called when a new data is available from the other side and the handshake did not complete. /// </summary> /// <param name="data">Adapter incoming data.</param> /// <exception cref="InvalidOperationException">Must call reset before attempting to receive a handshake message.</exception> public void HandShakeMessageDataReceived(byte[] data) { if (!_wasReset) { throw new InvalidOperationException("Must call reset before attempting to receive a handshake message."); } lock (_lock) { bool shouldSendRequest = State == ResonanceHandShakeState.Idle; State = ResonanceHandShakeState.InProgress; ResonanceHandShakeMessage message = HandShakeTranscoder.Decode(data); if (message.Type == ResonanceHandShakeMessageType.Request) { if (shouldSendRequest) { Logger.LogDebug("Sending Handshake Request..."); ResonanceHandShakeMessage r = new ResonanceHandShakeMessage(); r.Type = ResonanceHandShakeMessageType.Request; r.ClientId = ClientID; r.RequireEncryption = EncryptionEnabled; r.EncryptionPublicKey = _publicKey; OnWriteHandShake(HandShakeTranscoder.Encode(r)); Thread.Sleep(10); } else if (ClientID > message.ClientId) { Thread.Sleep(10); } var request = message; Logger.LogDebug("Handshake Request Received..."); ResonanceHandShakeMessage response = new ResonanceHandShakeMessage(); response.Type = ResonanceHandShakeMessageType.Response; response.ClientId = ClientID; if (EncryptionEnabled && request.RequireEncryption) { response.EncryptionPublicKey = _publicKey; response.RequireEncryption = true; if (ClientID > request.ClientId && State != ResonanceHandShakeState.Completed) { _symmetricPassword = Guid.NewGuid().ToString(); OnSymmetricPasswordAvailable(_symmetricPassword); response.SymmetricPassword = _cryptographyProvider.Encrypt(_symmetricPassword, request.EncryptionPublicKey); OnWriteHandShake(HandShakeTranscoder.Encode(response)); Logger.LogDebug("Handshake Response Sent..."); } } else { if (ClientID > request.ClientId && State != ResonanceHandShakeState.Completed) { OnWriteHandShake(HandShakeTranscoder.Encode(response)); Logger.LogDebug("Handshake Response Sent..."); } } } else if (message.Type == ResonanceHandShakeMessageType.Response) { var response = message; Logger.LogDebug("Handshake Response Received..."); if (response.RequireEncryption && EncryptionEnabled) { if (response.ClientId > ClientID) { _symmetricPassword = _cryptographyProvider.Decrypt(response.SymmetricPassword, _privateKey); OnSymmetricPasswordAvailable(_symmetricPassword); } } if (response.ClientId > ClientID) { State = ResonanceHandShakeState.Completed; OnWriteHandShake(HandShakeTranscoder.Encode(new ResonanceHandShakeMessage() { Type = ResonanceHandShakeMessageType.Complete, ClientId = ClientID })); Logger.LogDebug("Handshake completed."); Completed?.Invoke(this, new EventArgs()); } } else { State = ResonanceHandShakeState.Completed; Logger.LogDebug("Handshake completed."); Completed?.Invoke(this, new EventArgs()); } } }