/// <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");
}
}
}
}