/// <summary> /// Secures the message using the security token. /// </summary> protected BufferCollection WriteSymmetricMessage( uint messageType, uint requestId, ChannelToken token, object messageBody, bool isRequest, out bool limitsExceeded) { limitsExceeded = false; bool success = false; BufferCollection chunksToProcess = null; try { // calculate chunk sizes. int maxCipherTextSize = SendBufferSize - TcpMessageLimits.SymmetricHeaderSize; int maxCipherBlocks = maxCipherTextSize / EncryptionBlockSize; int maxPlainTextSize = maxCipherBlocks * EncryptionBlockSize; int maxPayloadSize = maxPlainTextSize - SymmetricSignatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int headerSize = TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize; // write the body to stream. ArraySegmentStream ostrm = new ArraySegmentStream( BufferManager, SendBufferSize, headerSize, maxPayloadSize); // check for encodeable body. IEncodeable encodeable = messageBody as IEncodeable; if (encodeable != null) { // debug code used to verify that message aborts are handled correctly. // int maxMessageSize = Quotas.MessageContext.MaxMessageSize; // Quotas.MessageContext.MaxMessageSize = Int32.MaxValue; BinaryEncoder.EncodeMessage(encodeable, ostrm, Quotas.MessageContext); // Quotas.MessageContext.MaxMessageSize = maxMessageSize; } // check for raw bytes. ArraySegment <byte>?rawBytes = messageBody as ArraySegment <byte>?; if (rawBytes != null) { BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); encoder.WriteRawBytes(rawBytes.Value.Array, rawBytes.Value.Offset, rawBytes.Value.Count); encoder.Close(); } chunksToProcess = ostrm.GetBuffers("WriteSymmetricMessage"); // ensure there is at least one chunk. if (chunksToProcess.Count == 0) { byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteSymmetricMessage"); chunksToProcess.Add(new ArraySegment <byte>(buffer, 0, 0)); } BufferCollection chunksToSend = new BufferCollection(chunksToProcess.Capacity); int messageSize = 0; for (int ii = 0; ii < chunksToProcess.Count; ii++) { ArraySegment <byte> chunkToProcess = chunksToProcess[ii]; // nothing more to do if limits exceeded. if (limitsExceeded) { BufferManager.ReturnBuffer(chunkToProcess.Array, "WriteSymmetricMessage"); continue; } MemoryStream strm = new MemoryStream(chunkToProcess.Array, 0, SendBufferSize); BinaryEncoder encoder = new BinaryEncoder(strm, Quotas.MessageContext); // check if the message needs to be aborted. if (MessageLimitsExceeded(isRequest, messageSize + chunkToProcess.Count - headerSize, ii + 1)) { encoder.WriteUInt32(null, messageType | TcpMessageType.Abort); // replace the body in the chunk with an error message. BinaryEncoder errorEncoder = new BinaryEncoder( chunkToProcess.Array, chunkToProcess.Offset, chunkToProcess.Count, Quotas.MessageContext); WriteErrorMessageBody(errorEncoder, (isRequest) ? StatusCodes.BadRequestTooLarge : StatusCodes.BadResponseTooLarge); int size = errorEncoder.Close(); chunkToProcess = new ArraySegment <byte>(chunkToProcess.Array, chunkToProcess.Offset, size); limitsExceeded = true; } // check if the message is complete. else if (ii == chunksToProcess.Count - 1) { encoder.WriteUInt32(null, messageType | TcpMessageType.Final); } // more chunks to follow. else { encoder.WriteUInt32(null, messageType | TcpMessageType.Intermediate); } int count = 0; count += TcpMessageLimits.SequenceHeaderSize; count += chunkToProcess.Count; count += SymmetricSignatureSize; // calculate the padding. int padding = 0; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // reserve one byte for the padding size. count++; if (count % EncryptionBlockSize != 0) { padding = EncryptionBlockSize - (count % EncryptionBlockSize); } count += padding; } count += TcpMessageLimits.SymmetricHeaderSize; encoder.WriteUInt32(null, (uint)count); encoder.WriteUInt32(null, ChannelId); encoder.WriteUInt32(null, token.TokenId); uint sequenceNumber = GetNewSequenceNumber(); encoder.WriteUInt32(null, sequenceNumber); encoder.WriteUInt32(null, requestId); // skip body. strm.Seek(chunkToProcess.Count, SeekOrigin.Current); // update message size count. messageSize += chunkToProcess.Count; // write padding. if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { for (int jj = 0; jj <= padding; jj++) { encoder.WriteByte(null, (byte)padding); } } if (SecurityMode != MessageSecurityMode.None) { // calculate and write signature. byte[] signature = Sign(token, new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position), isRequest); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } } if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // encrypt the data. ArraySegment <byte> dataToEncrypt = new ArraySegment <byte>(chunkToProcess.Array, TcpMessageLimits.SymmetricHeaderSize, encoder.Position - TcpMessageLimits.SymmetricHeaderSize); Encrypt(token, dataToEncrypt, isRequest); } // add the header into chunk. chunksToSend.Add(new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position)); } // ensure the buffers don't get cleaned up on exit. success = true; return(chunksToSend); } finally { if (!success) { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "WriteSymmetricMessage"); } } } }
/// <summary> /// Secures the message using the security token. /// </summary> protected BufferCollection WriteSymmetricMessage( uint messageType, uint requestId, TcpChannelToken token, object messageBody, bool isRequest, out bool limitsExceeded) { limitsExceeded = false; bool success = false; BufferCollection chunksToProcess = null; try { // calculate chunk sizes. int maxCipherTextSize = SendBufferSize - TcpMessageLimits.SymmetricHeaderSize; int maxCipherBlocks = maxCipherTextSize/EncryptionBlockSize; int maxPlainTextSize = maxCipherBlocks*EncryptionBlockSize; int maxPayloadSize = maxPlainTextSize - SymmetricSignatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int headerSize = TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize; // write the body to stream. ArraySegmentStream ostrm = new ArraySegmentStream( BufferManager, SendBufferSize, headerSize, maxPayloadSize); // check for encodeable body. IEncodeable encodeable = messageBody as IEncodeable; if (encodeable != null) { // debug code used to verify that message aborts are handled correctly. // int maxMessageSize = Quotas.MessageContext.MaxMessageSize; // Quotas.MessageContext.MaxMessageSize = Int32.MaxValue; BinaryEncoder.EncodeMessage(encodeable, ostrm, Quotas.MessageContext); // Quotas.MessageContext.MaxMessageSize = maxMessageSize; } // check for raw bytes. ArraySegment<byte>? rawBytes = messageBody as ArraySegment<byte>?; if (rawBytes != null) { BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); encoder.WriteRawBytes(rawBytes.Value.Array, rawBytes.Value.Offset, rawBytes.Value.Count); encoder.Close(); } chunksToProcess = ostrm.GetBuffers("WriteSymmetricMessage"); // ensure there is at least one chunk. if (chunksToProcess.Count == 0) { byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteSymmetricMessage"); chunksToProcess.Add(new ArraySegment<byte>(buffer, 0, 0)); } BufferCollection chunksToSend = new BufferCollection(chunksToProcess.Capacity); int messageSize = 0; for (int ii = 0; ii < chunksToProcess.Count; ii++) { ArraySegment<byte> chunkToProcess = chunksToProcess[ii]; // nothing more to do if limits exceeded. if (limitsExceeded) { BufferManager.ReturnBuffer(chunkToProcess.Array, "WriteSymmetricMessage"); continue; } MemoryStream strm = new MemoryStream(chunkToProcess.Array, 0, SendBufferSize); BinaryEncoder encoder = new BinaryEncoder(strm, Quotas.MessageContext); // check if the message needs to be aborted. if (MessageLimitsExceeded(isRequest, messageSize + chunkToProcess.Count - headerSize, ii+1)) { encoder.WriteUInt32(null, messageType | TcpMessageType.Abort); // replace the body in the chunk with an error message. BinaryEncoder errorEncoder = new BinaryEncoder( chunkToProcess.Array, chunkToProcess.Offset, chunkToProcess.Count, Quotas.MessageContext); WriteErrorMessageBody(errorEncoder, (isRequest)?StatusCodes.BadRequestTooLarge:StatusCodes.BadResponseTooLarge); int size = errorEncoder.Close(); chunkToProcess = new ArraySegment<byte>(chunkToProcess.Array, chunkToProcess.Offset, size); limitsExceeded = true; } // check if the message is complete. else if (ii == chunksToProcess.Count-1) { encoder.WriteUInt32(null, messageType | TcpMessageType.Final); } // more chunks to follow. else { encoder.WriteUInt32(null, messageType | TcpMessageType.Intermediate); } int count = 0; count += TcpMessageLimits.SequenceHeaderSize; count += chunkToProcess.Count; count += SymmetricSignatureSize; // calculate the padding. int padding = 0; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // reserve one byte for the padding size. count++; if (count%EncryptionBlockSize != 0) { padding = EncryptionBlockSize - (count%EncryptionBlockSize); } count += padding; } count += TcpMessageLimits.SymmetricHeaderSize; encoder.WriteUInt32(null, (uint)count); encoder.WriteUInt32(null, ChannelId); encoder.WriteUInt32(null, token.TokenId); uint sequenceNumber = GetNewSequenceNumber(); encoder.WriteUInt32(null, sequenceNumber); encoder.WriteUInt32(null, requestId); // skip body. strm.Seek(chunkToProcess.Count, SeekOrigin.Current); // update message size count. messageSize += chunkToProcess.Count; // write padding. if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { for (int jj = 0; jj <= padding; jj++) { encoder.WriteByte(null, (byte)padding); } } if (SecurityMode != MessageSecurityMode.None) { // calculate and write signature. byte[] signature = Sign(token, new ArraySegment<byte>(chunkToProcess.Array, 0, encoder.Position), isRequest); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } } if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // encrypt the data. ArraySegment<byte> dataToEncrypt = new ArraySegment<byte>(chunkToProcess.Array, TcpMessageLimits.SymmetricHeaderSize, encoder.Position-TcpMessageLimits.SymmetricHeaderSize); Encrypt(token, dataToEncrypt, isRequest); } // add the header into chunk. chunksToSend.Add(new ArraySegment<byte>(chunkToProcess.Array, 0, encoder.Position)); } // ensure the buffers don't get cleaned up on exit. success = true; return chunksToSend; } finally { if (!success) { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "WriteSymmetricMessage"); } } } }