/// <summary> /// Processes and stores the clients primary-stage Symmetric <see cref="KeyParams"/>, /// decrypted with the servers <see cref="IAsymmetricKeyPair">Asymmetric KeyPair</see>. /// </summary> /// /// <param name="PacketStream">A Stream containing the raw packet data</param> private void ProcessPrimary(MemoryStream PacketStream) { // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); // read the data byte[] data = new byte[pktHdr.PayloadLength]; PacketStream.Read(data, 0, data.Length); // decrypt using the auth stage symmetric key data = SymmetricTransform(_cltSymProcessor, data); // remove random padding data = UnwrapMessage(data); // decrypt the symmetric key using the primary asymmetric cipher byte[] dec = AsymmetricDecrypt(_srvAsmParams, _primKeyPair, data); // clear auth key _cltKeyParams.Dispose(); // deserialize the primary session key _cltKeyParams = KeyParams.DeSerialize(new MemoryStream(dec)); }
/// <summary> /// Processes the clients Primary-Stage <see cref="IAsymmetricKey">AsymmetricKey</see> Public key. /// </summary> /// /// <param name="PacketStream">A Stream containing the raw packet data</param> private void ProcessPrimeEx(MemoryStream PacketStream) { // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); // read the data byte[] data = new byte[pktHdr.PayloadLength]; PacketStream.Read(data, 0, data.Length); // use clients symmetric key to decrypt data byte[] dec = SymmetricTransform(_cltSymProcessor, data); // remove padding dec = UnwrapMessage(dec); MemoryStream cltStream = new MemoryStream(dec); // store the clients public key _cltPublicKey = GetAsymmetricPublicKey(cltStream, _cltAsmParams); }
/// <summary> /// Process the clients private identity. /// <para>Decrypts and stores the clients private identity using the clients Auth-Stage Symmetric Key.</para> /// </summary> /// /// <param name="PacketStream">A Stream containing the raw packet data</param> private void ProcessAuth(MemoryStream PacketStream) { // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); byte[] data = new byte[pktHdr.PayloadLength]; PacketStream.Read(data, 0, data.Length); // create the clients auth-stage symmetric cipher _cltSymProcessor = SymmetricInit(_cltIdentity.Session, _cltKeyParams); // decrypt the payload byte[] dec = SymmetricTransform(_cltSymProcessor, data); // remove random padding dec = UnwrapMessage(dec); // get the clients private id _cltIdentity = new DtmIdentity(new MemoryStream(dec)); // notify user long resp = 0; if (IdentityReceived != null) { DtmIdentityEventArgs args = new DtmIdentityEventArgs(DtmExchangeFlags.Auth, resp, _cltIdentity); IdentityReceived(this, args); resp = args.Flag; if (args.Cancel) { // back out of session TearDown(); } } }
/// <summary> /// Processes and stores the clients Auth-Stage Symmetric <see cref="KeyParams"/>, /// decrypted with the servers <see cref="IAsymmetricKeyPair">Asymmetric KeyPair</see>. /// </summary> /// /// <param name="PacketStream">A Stream containing the raw packet data</param> private void ProcessAuthEx(MemoryStream PacketStream) { // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); // read the data byte[] data = new byte[pktHdr.PayloadLength]; PacketStream.Read(data, 0, data.Length); // decrypt the symmetric key byte[] dec = AsymmetricDecrypt(_srvAsmParams, _authKeyPair, data); // deserialize the keyparams structure _cltKeyParams = KeyParams.DeSerialize(new MemoryStream(dec)); }
/// <summary> /// Resend a packet to a host /// </summary> private void Resend(DtmPacket Packet) { if (_sndBuffer.Exists(Packet.Sequence)) { _maxSendCounter++; // limit attack scope with session resend max if (_maxSendCounter > MaxResend) { // let the app decide what to do next DtmErrorEventArgs args = new DtmErrorEventArgs(new InvalidDataException("The stream has encountered data loss, attempting to resync.."), DtmErrorSeverity.DataLoss); if (SessionError != null) SessionError(this, args); if (args.Cancel) { Disconnect(); return; } } try { MemoryStream pktStm = _sndBuffer.Peek(Packet.Sequence); if (pktStm != null) { if (pktStm.Length > 0) pktStm.WriteTo(_clientSocket.TcpStream); _sndSequence++; } } catch { // packet lost, request a resync Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.DataLost); } } else { // packet lost, request a resync Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.DataLost); } }
/// <summary> /// Creates a serialized request packet (DtmPacket) /// </summary> private MemoryStream CreateRequest(DtmPacketTypes Message, short State) { MemoryStream ret = new DtmPacket(Message, 0, 0, State).ToStream(); ret.Seek(0, SeekOrigin.Begin); return ret; }
/// <summary> /// Used to read a blocking message response /// </summary> private MemoryStream BlockingReceive() { MemoryStream pktStm = null; try { // get the header pktStm = _clientSocket.GetStreamData(DtmPacket.GetHeaderSize(), EXCHTIMEOUT); DtmPacket pktHdr = new DtmPacket(pktStm); // add the payload if (pktHdr.PayloadLength > 0) _clientSocket.GetStreamData((int)pktHdr.PayloadLength, EXCHTIMEOUT).WriteTo(pktStm); pktStm.Seek(0, SeekOrigin.Begin); } catch (ObjectDisposedException) { // host is disconnected, notify app DtmErrorEventArgs args = new DtmErrorEventArgs(new SocketException((int)SocketError.HostDown), DtmErrorSeverity.Connection); if (SessionError != null) SessionError(this, args); if (args.Cancel == true) Disconnect(); } if (pktStm == null || pktStm.Length == 0) { // exchange failed if (SessionError != null) SessionError(this, new DtmErrorEventArgs(new SocketException((int)SocketError.HostUnreachable), DtmErrorSeverity.Critical)); Disconnect(); } return pktStm; }
/// <summary> /// Used Post-Exchange to setup a file transfer from the remote host /// </summary> /// /// <param name="PacketStream">The stream containing the file transfer request</param> private void ReceiveFile(Stream PacketStream) { // asynchronous transfer by sending a file key and info, and running the entire transfer on another socket.. if (!_isEstablished) throw new CryptoProcessingException("DtmKex:ReceiveFile", "The VPN has not been established!", new InvalidOperationException()); if (FileRequest == null) throw new CryptoProcessingException("DtmKex:ReceiveFile", "The FileRequest and FileReceived must be connected to perform a file transfer, read the documentation!", new InvalidOperationException()); if (FileReceived == null) throw new CryptoProcessingException("DtmKex:ReceiveFile", "The FileRequest and FileReceived must be connected to perform a file transfer, read the documentation!", new InvalidOperationException()); // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); // read the packet byte[] enc = new byte[pktHdr.PayloadLength]; // get the encrypted data PacketStream.Read(enc, 0, enc.Length); // decrypt it using client crypto processor byte[] dec = SymmetricTransform(_cltSymProcessor, enc); // remove padding dec = UnwrapMessage(dec); MemoryStream pktStm = new MemoryStream(dec); // get file info header DtmFileInfo pktFi = new DtmFileInfo(pktStm); // get the key KeyParams fileKey = KeyParams.DeSerialize(pktStm); // forward request to app DtmFileRequestEventArgs args = new DtmFileRequestEventArgs(pktFi.FileName); FileRequest(this, args); // accept file or refuse and exit; app must send back a valid path or cancel; if cancel, send a refuse notice which will signal the end of the transfer, otherwise store file path and port if (args.Cancel || string.IsNullOrEmpty(args.FilePath) || args.FilePath.Equals(pktFi.FileName) || !Directory.Exists(Path.GetDirectoryName(args.FilePath))) { // send refuse and exit Transmit(DtmPacketTypes.Transfer, (short)DtmTransferFlags.Refused, pktHdr.OptionFlag); } else { // create the files crypto processor ICipherMode fileSymProcessor = SymmetricInit(_cltIdentity.Session, fileKey); // enable parallel decryption int blockSize = ((int)MessageBufferSize - DtmPacket.GetHeaderSize()) - ((int)MessageBufferSize - DtmPacket.GetHeaderSize()) % ((CTR)fileSymProcessor).ParallelMinimumSize; ((CTR)fileSymProcessor).ParallelBlockSize = blockSize; // init the file transfer host DtmFileTransfer fileTransfer = new DtmFileTransfer(fileSymProcessor, pktHdr.OptionFlag, 1024, (int)FileBufferSize); fileTransfer.FileTransferred += new DtmFileTransfer.FileTransferredDelegate(OnFileReceived); fileTransfer.ProgressPercent += new DtmFileTransfer.ProgressDelegate(OnFileReceivedProgress); // add to dictionary _transQueue.TryAdd(pktHdr.OptionFlag, fileTransfer); try { // start the transfer on a new thread Task socketTask = Task.Factory.StartNew(() => { fileTransfer.StartReceive(_clientSocket.RemoteAddress, (int)pktFi.OptionsFlag, args.FilePath); }); socketTask.Wait(10); } catch (AggregateException ae) { if (SessionError != null) SessionError(this, new DtmErrorEventArgs(ae.GetBaseException(), DtmErrorSeverity.Warning)); } } }
private void Transmit(DtmPacketTypes PacketType, short PacketFlag, long OptionFlag = 0, MemoryStream Payload = null) { lock (_sndLock) { long pldLen = Payload == null ? 0 : Payload.Length; // create a new packet: packet flag, payload size, sequence, and state flag MemoryStream pktStm = new DtmPacket(PacketType, pldLen, _sndSequence, PacketFlag, OptionFlag).ToStream(); // add payload if (Payload != null) { // copy to output pktStm.Seek(0, SeekOrigin.End); Payload.WriteTo(pktStm); pktStm.Seek(0, SeekOrigin.Begin); } // store in the file packet buffer _sndBuffer.Push(_sndSequence, pktStm); // increment file send counter _sndSequence++; // transmit to remote client if (_clientSocket.IsConnected) { try { _clientSocket.SendAsync(pktStm); } catch (CryptoSocketException ce) { SocketException se = ce.InnerException as SocketException; if (se.SocketErrorCode == SocketError.WouldBlock || se.SocketErrorCode == SocketError.IOPending || se.SocketErrorCode == SocketError.NoBufferSpaceAvailable) { // buffer is full, throttle down Throttle(pktStm); } else { // possible connection dropped, alert app if (SessionError != null) { DtmErrorEventArgs args = new DtmErrorEventArgs(ce, DtmErrorSeverity.Connection); SessionError(this, args); } } } catch (Exception) { } // notify app if (PacketSent != null) PacketSent(this, new DtmPacketEventArgs((short)DtmTransferFlags.DataChunk, pldLen)); } } }
/// <summary> /// Blocking transceiver; sends a packet and waits for a response. /// <para>For use with time sensitive data, that requires fast synchronous processing. /// Sent and received packets are not queued or buffered.</para> /// </summary> /// /// <param name="DataStream">The payload data to send to the remote host</param> /// <param name="TimeOut">The number of milliseconds to wait before timing out (default is infinite)</param> /// /// <returns>The return streams decrypted payload data, or an empty stream on failure</returns> public MemoryStream SendReceive(MemoryStream DataStream, int TimeOut = Timeout.Infinite) { if (!_isEstablished) throw new CryptoProcessingException("DtmKex:SendReceive", "The VPN is not established!", new InvalidOperationException()); byte[] data = DataStream.ToArray(); // append/prepend random data = WrapMessage(data, _dtmParameters.MaxMessageAppend, _dtmParameters.MaxMessagePrePend); // encrypt the data with the clients symmetric processor byte[] enc = SymmetricTransform(_srvSymProcessor, data); // store total bytes sent _bytesSent += enc.Length; // optional delay before transmission if (_dtmParameters.MaxMessageDelayMS > 0) SendWait(_dtmParameters.MaxMessageDelayMS); // create the packet MemoryStream pktStm = new DtmPacket(DtmPacketTypes.Message, enc.Length, _sndSequence, (short)DtmMessageFlags.Transmission).ToStream(); pktStm.Seek(0, SeekOrigin.End); pktStm.Write(enc, 0, enc.Length); pktStm.Seek(0, SeekOrigin.Begin); // transmit data _clientSocket.Send(pktStm); _sndSequence++; // wait for response pktStm = BlockingReceive(); // get the header DtmPacket dtmHdr = new DtmPacket(pktStm); // payload buffer data = new byte[dtmHdr.PayloadLength]; // copy data to buffer pktStm.Write(data, 0, data.Length); // decrypt response data = SymmetricTransform(_cltSymProcessor, data); // remove padding data = UnwrapMessage(data); // increment rcv counter _rcvSequence++; // record encrypted byte count for resync _bytesReceived += dtmHdr.PayloadLength; return new MemoryStream(data); }
private void Resend(DtmPacket PacketHeader) { if (_sndBuffer.Exists(PacketHeader.Sequence)) { MemoryStream pktStm = _sndBuffer.Peek(PacketHeader.Sequence); if (pktStm != null) { if (pktStm.Length > 0) pktStm.WriteTo(_clientSocket.TcpStream); } } }
private void Receive(Stream PacketStream) { // get the packet header DtmPacket prcPacket = new DtmPacket(PacketStream); // read the packet byte[] enc = new byte[prcPacket.PayloadLength]; // get the encrypted data PacketStream.Read(enc, 0, enc.Length); // decrypt it using file crypto processor byte[] dec = SymmetricTransform(_fileSymProcessor, enc); // get file info header DtmFileInfo pktFi = new DtmFileInfo(dec); // store file name and size string fileName = pktFi.FileName; long fileSize = pktFi.FileSize; long streamLen = 0; try { using (FileStream outStream = new FileStream(_tempPath, FileMode.Append, FileAccess.Write, FileShare.Read)) { // calculate offsets int hdrSize = pktFi.GetHeaderSize(); int len = dec.Length - hdrSize; // write to file outStream.Write(ArrayUtils.GetRange(dec, hdrSize, len), 0, len); // store length streamLen = outStream.Length; // progress if (ProgressPercent != null) { double progress = 100.0 * (double)pktFi.OptionsFlag / fileSize; ProgressPercent(this, new System.ComponentModel.ProgressChangedEventArgs((int)progress, (object)fileSize)); } } // transfer completed if (streamLen == fileSize) { // reset attributes File.SetAttributes(_tempPath, File.GetAttributes(_tempPath) & ~FileAttributes.Hidden); // rename the file File.Move(_tempPath, VTDev.Libraries.CEXEngine.Tools.FileTools.GetUniqueName(_filePath)); // notify app if (FileTransferred != null) FileTransferred(this, new DtmPacketEventArgs((short)DtmTransferFlags.Received, prcPacket.OptionFlag)); // flush and close ReceiveClose(); } } catch (Exception ex) { throw new CryptoFileTransferException("DtmFileTransfer:Receive", "The file transfer did not complete!", ex); } }
private void Process(MemoryStream PacketStream) { // increment rcv sequence _rcvSequence++; // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); PacketStream.Seek(0, SeekOrigin.Begin); switch (pktHdr.PacketType) { // file transfer case DtmPacketTypes.Transfer: { switch ((DtmTransferFlags)pktHdr.PacketFlag) { case DtmTransferFlags.DataChunk: { try { lock (_rcvLock) { // received file data Receive(PacketStream); } } catch (Exception) { // packet corrupted, request a retransmission and exit Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resend, pktHdr.Sequence); return; } // echo the packet to remove it from remote buffer Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Echo, pktHdr.Sequence); break; } } break; } // service messages case DtmPacketTypes.Service: { switch ((DtmServiceFlags)pktHdr.PacketFlag) { case DtmServiceFlags.Resend: { // resend the packet Resend(pktHdr); break; } case DtmServiceFlags.Echo: { // remove from local buffer if (_sndBuffer.Exists(pktHdr.OptionFlag)) _sndBuffer.Destroy(pktHdr.OptionFlag); break; } } break; } default: { throw new CryptoKeyExchangeException("DtmFileTransfer:Process", "The packet type is unknown!", new InvalidDataException()); } } // notify parent if (PacketReceived != null) PacketReceived(this, new DtmPacketEventArgs(pktHdr.PacketFlag, pktHdr.PayloadLength)); }
/// <summary> /// Used to process a resync response. /// <para>The remote host has sent the number of bytes encrypted as the OptionFlag in the DtmPacket. /// The resynchronization of the crypto stream involves first encrypting an equal sized array, /// and then testing for validity by decrypting the payload and comparing it to the stored client id. /// If the Resync fails, the client Disconnects, notifies the application, and performs a teardown of the VPN.</para> /// </summary> /// /// <param name="PacketStream">A resync packet</param> private void ProcessResync(MemoryStream PacketStream) { // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); int len = (int)(pktHdr.OptionFlag - pktHdr.PayloadLength - _bytesReceived); if (len > 0) { byte[] pad = new byte[len]; // sync the cipher stream SymmetricTransform(_cltSymProcessor, pad); } else if (len < 0) { // can't resync, alert user and disconnect DtmErrorEventArgs args = new DtmErrorEventArgs(new InvalidDataException("The data stream could not be resynced, connection aborted!"), DtmErrorSeverity.Critical); if (SessionError != null) SessionError(this, args); Disconnect(); return; } // read the packet byte[] data = new byte[pktHdr.PayloadLength]; // get the encrypted data PacketStream.Read(data, 0, data.Length); // decrypt the payload byte[] id = SymmetricTransform(_cltSymProcessor, data); // remove random padding id = UnwrapMessage(id); // compare to stored id if (!ArrayUtils.AreEqual(id, _cltIdentity.Identity)) { // resync failed, abort connection DtmErrorEventArgs args = new DtmErrorEventArgs(new InvalidDataException("The data stream could not be resynced, connection aborted!"), DtmErrorSeverity.Critical); if (SessionError != null) SessionError(this, args); Disconnect(); return; } }
/// <summary> /// Notify that the VPN is established /// </summary> /// /// <returns>A Stream containing the raw packet data</returns> private MemoryStream CreateEstablish() { MemoryStream pktStm = new DtmPacket(DtmPacketTypes.Exchange, 0, _sndSequence, (short)DtmExchangeFlags.Established).ToStream(); // notify if (PacketSent != null) PacketSent(this, new DtmPacketEventArgs((short)_exchangeState, pktStm.Length)); // stage completed _exchangeState = DtmExchangeFlags.Established; return pktStm; }
/// <summary> /// Process the clients identity structure <see cref="DtmIdentity"/>. /// </summary> /// /// <param name="PacketStream">A Stream containing the raw packet data</param> private void ProcessSync(MemoryStream PacketStream) { // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); // read the data byte[] data = new byte[pktHdr.PayloadLength]; PacketStream.Read(data, 0, data.Length); // use clients symmetric key to decrypt data byte[] dec = SymmetricTransform(_cltSymProcessor, data); // remove random padding dec = UnwrapMessage(dec); // get the identity _cltIdentity = new DtmIdentity(dec); // pass id to the client, include oid long resp = 0; if (IdentityReceived != null) { DtmIdentityEventArgs args = new DtmIdentityEventArgs(DtmExchangeFlags.Init, _cltIdentity.OptionFlag, _cltIdentity); IdentityReceived(this, args); resp = args.Flag; if (args.Cancel) { // back out of session TearDown(); } } // get the params oid _cltAsmParams = GetAsymmetricParams(_cltIdentity.PkeId); }
/// <summary> /// Process a message. /// <para>Use this method to process <see cref="DtmPacket"/> data sent to the server</para> /// </summary> private void Process(MemoryStream PacketStream) { try { // increment rcv sequence _rcvSequence++; // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); PacketStream.Seek(0, SeekOrigin.Begin); switch (pktHdr.PacketType) { // message stream case DtmPacketTypes.Message: { // process message switch ((DtmMessageFlags)pktHdr.PacketFlag) { case DtmMessageFlags.Transmission: { try { // received stream data ReceiveMessage(PacketStream); } catch (Exception) { // packet corrupted, request a retransmission and exit Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resend, pktHdr.Sequence); return; } // echo the packet to remove it from remote buffer Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Echo, pktHdr.Sequence); break; } } break; } // service messages case DtmPacketTypes.Service: { switch ((DtmServiceFlags)pktHdr.PacketFlag) { case DtmServiceFlags.KeepAlive: { // reset the keep alive counter _pulseCounter = 0; break; } // process echo case DtmServiceFlags.Echo: { // remove from buffer if (_sndBuffer.Exists(pktHdr.OptionFlag)) _sndBuffer.Destroy(pktHdr.OptionFlag); break; } case DtmServiceFlags.Resend: { // attempt resend, if not in buffer transmission, attempts a resync Resend(pktHdr); break; } case DtmServiceFlags.DataLost: { // remote packet lost, try resync. note: if this happens often, increase buffer size in ctor + tcp MemoryStream pktData = CreateResync(); _bytesSent += pktData.Length; Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resync, _bytesSent, pktData); break; } case DtmServiceFlags.Resync: { // attempt to resync the crypto stream ProcessResync(PacketStream); break; } case DtmServiceFlags.Refusal: { DtmErrorEventArgs args = new DtmErrorEventArgs(new ApplicationException("The session was refused by the remote host."), DtmErrorSeverity.Connection); if (SessionError != null) SessionError(this, args); if (args.Cancel) Disconnect(); break; } case DtmServiceFlags.Terminate: { // reserved DtmErrorEventArgs args = new DtmErrorEventArgs(new ApplicationException("The session was terminated by the remote host."), DtmErrorSeverity.Critical); if (SessionError != null) SessionError(this, args); Disconnect(); break; } } break; } // file transfer case DtmPacketTypes.Transfer: { switch ((DtmTransferFlags)pktHdr.PacketFlag) { case DtmTransferFlags.Request: { // received file transfer request ReceiveFile(PacketStream); break; } case DtmTransferFlags.Refused: { // refused by remote DtmErrorEventArgs args = new DtmErrorEventArgs(new ApplicationException("The session was refused by the remote host."), DtmErrorSeverity.Connection); if (SessionError != null) SessionError(this, args); CloseTransfer(pktHdr.OptionFlag); break; } case DtmTransferFlags.Received: { // refused by remote CloseTransfer(pktHdr.OptionFlag); break; } } break; } // key exchange case DtmPacketTypes.Exchange: { // process message switch ((DtmExchangeFlags)pktHdr.PacketFlag) { case DtmExchangeFlags.Connect: { // received public id ProcessConnect(PacketStream); break; } case DtmExchangeFlags.Init: { // received auth-stage params ProcessInit(PacketStream); break; } case DtmExchangeFlags.PreAuth: { // received public key ProcessPreAuth(PacketStream); break; } case DtmExchangeFlags.AuthEx: { // received symmetric key ProcessAuthEx(PacketStream); break; } case DtmExchangeFlags.Auth: { // received private id ProcessAuth(PacketStream); break; } case DtmExchangeFlags.Sync: { // received primary public key params ProcessSync(PacketStream); break; } case DtmExchangeFlags.Primary: { // received primary public key ProcessPrimary(PacketStream); break; } case DtmExchangeFlags.PrimeEx: { // received primary session key ProcessPrimeEx(PacketStream); break; } case DtmExchangeFlags.Established: { // received ack established ProcessEstablish(PacketStream); break; } } break; } default: { if (SessionError != null) SessionError(this, new DtmErrorEventArgs(new CryptoProcessingException("DtmKex:Process", "The data transmission encountered an error!", new InvalidDataException()), DtmErrorSeverity.Critical)); break; } } // notify app if (PacketReceived != null) PacketReceived(this, new DtmPacketEventArgs(pktHdr.PacketFlag, pktHdr.PayloadLength)); } catch (Exception ex) { if (SessionError != null) SessionError(this, new DtmErrorEventArgs(new CryptoProcessingException("DtmKex:Process", "The data transmission encountered an error!", ex), DtmErrorSeverity.Critical)); } }
/// <summary> /// Used Post-Exchange to decrypt bytes received from the client /// </summary> /// /// <param name="PacketStream">The stream containing the ciphertext</param> private void ReceiveMessage(Stream PacketStream) { if (!_isEstablished) throw new CryptoProcessingException("DtmKex:Receive", "The VPN has not been established!", new InvalidOperationException()); // get the header DtmPacket pktHdr = new DtmPacket(PacketStream); // store total bytes received _bytesReceived += pktHdr.PayloadLength; byte[] enc = new byte[pktHdr.PayloadLength]; // get the encrypted data PacketStream.Read(enc, 0, enc.Length); // decrypt it using servers processor byte[] dec = SymmetricTransform(_cltSymProcessor, enc); // remove padding dec = UnwrapMessage(dec); // return the data if (DataReceived != null) { DtmDataReceivedEventArgs args = new DtmDataReceivedEventArgs(new MemoryStream(dec), 0); DataReceived(this, args); } }
/// <summary> /// Processes and queues incoming packets /// </summary> private void ProcessAndPush(PacketBuffer Buffer, MemoryStream PacketStream) { int hdrLen = DtmPacket.GetHeaderSize(); int pktLen = 0; // process the whole packet PacketStream.Seek(0, SeekOrigin.Begin); // get the header DtmPacket dtmPkt = new DtmPacket(PacketStream); PacketStream.Seek(0, SeekOrigin.Begin); // track high sequence number, filters corrupt packets if (dtmPkt.Sequence > _seqCounter && dtmPkt.PayloadLength < MAXRCVBUFFER && dtmPkt.OptionFlag < 1000) _seqCounter = dtmPkt.Sequence; // out of sync, possible packet loss if (_seqCounter - _rcvSequence > ResendThreshold) { // request a retransmission Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resend, _rcvSequence + 1); } if (dtmPkt.PayloadLength + hdrLen == PacketStream.Length) { // resend was already processed if (dtmPkt.Sequence < _rcvSequence) return; // push onto buffer Buffer.Push(dtmPkt.Sequence, PacketStream); } // more than one packet else if (dtmPkt.PayloadLength + hdrLen < PacketStream.Length) { byte[] buffer; long pos = 0; do { // get packet position and size pos = PacketStream.Position; if (PacketStream.Length - pos < DtmPacket.GetHeaderSize()) { // next packet corrupted, request a retransmission and exit Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resend, Buffer.GetHighKey() + 1); return; } dtmPkt = new DtmPacket(PacketStream); pktLen = (int)(hdrLen + dtmPkt.PayloadLength); if (pktLen > MAXRCVBUFFER || pktLen < 0 || PacketStream.Length - pos < pktLen) { // packet corrupted, request a retransmission and exit Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resend, Buffer.GetHighKey() + 1); return; } else { // create the buffer buffer = new byte[pktLen]; PacketStream.Seek(pos, SeekOrigin.Begin); PacketStream.Read(buffer, 0, (int)pktLen); // push onto buffer Buffer.Push(dtmPkt.Sequence, new MemoryStream(buffer)); } } while (PacketStream.Position < PacketStream.Length); } // malformed packet, send retransmit request else if (dtmPkt.PayloadLength > MAXRCVBUFFER || dtmPkt.PayloadLength < 0 || dtmPkt.PayloadLength + hdrLen > PacketStream.Length) { // packet corrupted, request a retransmission of last in queue + 1 Transmit(DtmPacketTypes.Service, (short)DtmServiceFlags.Resend, Buffer.GetHighKey() + 1); } }
/// <summary> /// Frame and Transmit the packet to the remote client /// </summary> /// /// <param name="PacketType">The packet class</param> /// <param name="PacketFlag">The packet message type flag</param> /// <param name="OptionFlag">The option flag</param> /// <param name="Payload">The packet payload flag</param> /// <param name="Blocking">Blocking or Async transmit</param> private void Transmit(DtmPacketTypes PacketType, short PacketFlag, long OptionFlag = 0, MemoryStream Payload = null, bool Blocking = false) { lock (_sendLock) { long pldLen = Payload == null ? 0 : Payload.Length; // create a new packet: packet flag, payload size, sequence, and state flag MemoryStream pktStm = new DtmPacket(PacketType, pldLen, _sndSequence, PacketFlag, OptionFlag).ToStream(); // add payload if (Payload != null) { // store total encrypted bytes sent if (_isEstablished) _bytesSent += Payload.Length; // copy to output pktStm.Seek(0, SeekOrigin.End); Payload.WriteTo(pktStm); pktStm.Seek(0, SeekOrigin.Begin); } // service requests are not buffered if (PacketType != DtmPacketTypes.Service) { // store in the packet buffer _sndBuffer.Push(_sndSequence, pktStm); } // increment send counter _sndSequence++; // transmit to remote client if (_clientSocket.IsConnected) { if (Blocking) { try { _clientSocket.SendAsync(pktStm); } catch (CryptoSocketException ce) { SocketException se = ce.InnerException as SocketException; if (se.SocketErrorCode == SocketError.WouldBlock || se.SocketErrorCode == SocketError.IOPending || se.SocketErrorCode == SocketError.NoBufferSpaceAvailable) { // buffer is full, slow down Throttle(pktStm); } else if (se.SocketErrorCode != SocketError.Success) { // possible connection dropped, alert app if (SessionError != null) { DtmErrorEventArgs args = new DtmErrorEventArgs(ce, DtmErrorSeverity.Connection); SessionError(this, args); if (args.Cancel == true) Disconnect(); } } } } else { try { pktStm.WriteTo(_clientSocket.TcpStream); } catch (Exception ex) { // internal error, alert app if (SessionError != null) { DtmErrorEventArgs args = new DtmErrorEventArgs(ex, DtmErrorSeverity.Critical); SessionError(this, args); if (args.Cancel == true) Disconnect(); } } } // notify app if (PacketSent != null) PacketSent(this, new DtmPacketEventArgs((short)_exchangeState, pldLen)); } else { // possible connection dropped, alert app if (SessionError != null) { DtmErrorEventArgs args = new DtmErrorEventArgs(new SocketException((int)SocketError.ConnectionReset), DtmErrorSeverity.Connection); SessionError(this, args); if (args.Cancel == true) Disconnect(); } } } }
/// <summary> /// Serialize a <see cref="DtmPacket"/> structure /// </summary> /// /// <param name="Packet">A CTKEPacket structure</param> /// /// <returns>A stream containing the CTKEPacket data</returns> public static Stream Serialize(DtmPacket Packet) { return Packet.ToStream(); }