Represents a security token associate with a channel.
        /// <summary>
        /// Activates a new token.
        /// </summary>
        protected void ActivateToken(TcpChannelToken token)
        {
            // compute the keys for the token.
            ComputeKeys(token);

            m_previousToken = m_currentToken;
            m_currentToken  = token;
        }
        /// <summary>
        /// Activates a new token.
        /// </summary>
        protected void ActivateToken(TcpChannelToken token)
        {
            // compute the keys for the token.
            ComputeKeys(token);

            m_previousToken = m_currentToken;
            m_currentToken  = token;
            m_renewedToken  = null;

            Utils.Trace("Token #{0} activated. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime);
        }
        /// <summary>
        /// Creates a new token.
        /// </summary>
        protected TcpChannelToken CreateToken()
        {
            TcpChannelToken token = new TcpChannelToken();

            token.ChannelId = m_channelId;
            token.TokenId   = 0;
            token.CreatedAt = DateTime.UtcNow;
            token.Lifetime  = (int)Quotas.SecurityTokenLifetime;

            return token;
        }
        /// <summary>
        /// Creates a new token.
        /// </summary>
        protected TcpChannelToken CreateToken()
        {
            TcpChannelToken token = new TcpChannelToken();

            token.ChannelId = m_channelId;
            token.TokenId   = 0;
            token.CreatedAt = DateTime.UtcNow;
            token.Lifetime  = (int)Quotas.SecurityTokenLifetime;

            Utils.Trace("Token #{0} created. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime);

            return token;
        }
        /// <summary>
        /// Decrypts the data in a buffer using symmetric encryption.
        /// </summary>
        protected void Decrypt(TcpChannelToken token, ArraySegment<byte> dataToDecrypt, bool useClientKeys)
        {
            switch (SecurityPolicyUri)
            {
                default:
                case SecurityPolicies.None:  
                {
                    break;
                }

                case SecurityPolicies.Basic256:
                case SecurityPolicies.Basic128Rsa15:
                {
                    SymmetricDecrypt(token, dataToDecrypt, useClientKeys);
                    break;
                }
            }
        }
        /// <summary>
        /// Returns the symmetric signature for the data.
        /// </summary>
        protected bool Verify(
            TcpChannelToken    token,
            byte[]             signature,
            ArraySegment<byte> dataToVerify,
            bool               useClientKeys)
        {       
            // verify signature.
            switch (SecurityPolicyUri)
            {
                case SecurityPolicies.None:
                {
                    return true;
                }

                case SecurityPolicies.Basic128Rsa15:
                case SecurityPolicies.Basic256:
                {
                    return SymmetricVerify(token, signature, dataToVerify, useClientKeys);
                }

                default:
                {
                    return false;
                }
            }
        }
        protected ArraySegment<byte> ReadSymmetricMessage(
            ArraySegment<byte>  buffer,
            bool                isRequest,
            out TcpChannelToken token,
            out uint            requestId,
            out uint            sequenceNumber)
        {            
            BinaryDecoder decoder = new BinaryDecoder(buffer.Array, buffer.Offset, buffer.Count, Quotas.MessageContext);
                      
            uint messageType = decoder.ReadUInt32(null);
            uint messageSize = decoder.ReadUInt32(null);
            uint channelId   = decoder.ReadUInt32(null);
            uint tokenId     = decoder.ReadUInt32(null);

            // ensure the channel is valid.
            if (channelId != ChannelId)
            {
                throw ServiceResultException.Create(
                    StatusCodes.BadTcpSecureChannelUnknown,
                    "SecureChannelId is not known. ChanneId={0}, CurrentChannelId={1}", 
                    channelId,
                    ChannelId);
            }

            // check for a message secured with the new token.
            if (RenewedToken != null && RenewedToken.TokenId == tokenId)
            {
                ActivateToken(RenewedToken);
            }

            // check if activation of the new token should be forced.
            if (RenewedToken != null && CurrentToken.ActivationRequired)
            {
                ActivateToken(RenewedToken);

                Utils.Trace("Token #{0} activated forced.", CurrentToken.TokenId);
            }

            // check for valid token.
            TcpChannelToken currentToken = CurrentToken;

            if (currentToken == null)
            {
                throw new ServiceResultException(StatusCodes.BadSecureChannelClosed);
            }

            // find the token.
            if (currentToken.TokenId != tokenId && PreviousToken != null && PreviousToken.TokenId != tokenId)
            {
                throw ServiceResultException.Create(
                    StatusCodes.BadTcpSecureChannelUnknown,
                    "TokenId is not known. ChanneId={0}, TokenId={1}, CurrentTokenId={2}, PreviousTokenId={3}", 
                    channelId, 
                    tokenId,
                    currentToken.TokenId,
                    (PreviousToken != null)?(int)PreviousToken.TokenId:-1);
            }

            token = currentToken;

            // check for a message secured with the token before it expired.
            if (PreviousToken != null && PreviousToken.TokenId == tokenId)
            {
                token = PreviousToken;
            }
            
            // check if token has expired.
            if (token.Expired)
            {
                throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Token #{0} has expired. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime);
            }

            int headerSize = decoder.Position;

            if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
            {
                // decrypt the message.
                Decrypt(token, new ArraySegment<byte>(buffer.Array, buffer.Offset + headerSize, buffer.Count - headerSize), isRequest);
            }
      
            if (SecurityMode != MessageSecurityMode.None)
            {
                // extract signature.
                byte[] signature = new byte[SymmetricSignatureSize];

                for (int ii = 0; ii < SymmetricSignatureSize; ii++)
                {
                    signature[ii] = buffer.Array[buffer.Offset+buffer.Count-SymmetricSignatureSize+ii];
                }
                
                // verify the signature.                                    
                if (!Verify(token, signature, new ArraySegment<byte>(buffer.Array, buffer.Offset, buffer.Count - SymmetricSignatureSize), isRequest))
                {
                    Utils.Trace("Could not verify signature on message.");
                    throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the signature on the message.");
                }
             }
            
            int paddingCount = 0;
      
            if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
            {
                // verify padding.
                int paddingStart = buffer.Offset + buffer.Count - SymmetricSignatureSize - 1;
                paddingCount = buffer.Array[paddingStart];

                for (int ii = paddingStart - paddingCount; ii < paddingStart; ii++)
                {
                    if (buffer.Array[ii] != paddingCount)
                    {
                        throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the padding in the message.");
                    }
                }

                // add byte for size.
                paddingCount++;
            }

            // extract request id and sequence number.
            sequenceNumber = decoder.ReadUInt32(null);
            requestId = decoder.ReadUInt32(null);

            // return an the data contained in the message.
            int startOfBody = buffer.Offset + TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize;
            int sizeOfBody  = buffer.Count - TcpMessageLimits.SymmetricHeaderSize - TcpMessageLimits.SequenceHeaderSize - paddingCount - SymmetricSignatureSize;

            return new ArraySegment<byte>(buffer.Array, startOfBody, sizeOfBody);
        }
        /// <summary>
        /// Returns the symmetric signature for the data.
        /// </summary>
        protected byte[] Sign(TcpChannelToken token, ArraySegment<byte> dataToSign, bool useClientKeys)
        {
            switch (SecurityPolicyUri)
            {
                default:
                case SecurityPolicies.None:           
                {
                    return null;
                }

                case SecurityPolicies.Basic128Rsa15:
                case SecurityPolicies.Basic256:
                {
                    return SymmetricSign(token, dataToSign, useClientKeys);
                }
            }
        }
        /// <summary>
        /// Decrypts a message using a symmetric algorithm.
        /// </summary>
        private static void SymmetricDecrypt(
            TcpChannelToken    token, 
            ArraySegment<byte> dataToDecrypt,
            bool               useClientKeys)
        {
            // get the encrypting key.
            SymmetricAlgorithm encryptingKey = (useClientKeys)?token.ClientEncryptor:token.ServerEncryptor;

            if (encryptingKey == null)
            {
                throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Token missing symmetric key object.");
            }

            ICryptoTransform decryptor = encryptingKey.CreateDecryptor();        

            try
            {          
                byte[] blockToDecrypt = dataToDecrypt.Array;

                int start = dataToDecrypt.Offset;
                int count = dataToDecrypt.Count;
                
                if (count % decryptor.InputBlockSize != 0)
                {
                    throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Input data is not an even number of encryption blocks.");
                }

                decryptor.TransformBlock(blockToDecrypt, start, count, blockToDecrypt, start);
            }
            finally
            {
                decryptor.Dispose();
            }                
        }        
Beispiel #10
0
        /// <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");
                    }
                }
            }
        }
Beispiel #11
0
        /// <summary>
        /// Binds a new socket to an existing channel.
        /// </summary>
        internal bool ReconnectToExistingChannel(
            TcpMessageSocket         socket, 
            uint                     requestId,
            uint                     sequenceNumber,
            uint                     channelId,
            X509Certificate2         clientCertificate, 
            TcpChannelToken          token,
            OpenSecureChannelRequest request)
        {            
            TcpServerChannel channel = null;

            lock (m_lock)
            {
                if (!m_channels.TryGetValue(channelId, out channel))
                {
                    throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Could not find secure channel referenced in the OpenSecureChannel request.");
                }
            }
                       
            channel.Reconnect(socket, requestId, sequenceNumber, clientCertificate, token, request);
            // Utils.Trace("Channel {0} reconnected", channelId);
            return true;
        }
        /// <summary>
        /// Sets the renewed token
        /// </summary>
        protected void SetRenewedToken(TcpChannelToken token)
        {
            m_renewedToken = token;

            Utils.Trace("RenewedToken #{0} set. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime);
        }
Beispiel #13
0
        /// <summary>
        /// Processes an OpenSecureChannel request message.
        /// </summary>
        private bool ProcessOpenSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk)
        {
            // validate the channel state.
            if (State != TcpChannelState.Opening && State != TcpChannelState.Open)
            {
                ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid,
                                  "Client sent an unexpected OpenSecureChannel message.");
                return(false);
            }

            // parse the security header.
            uint             channelId         = 0;
            X509Certificate2 clientCertificate = null;
            uint             requestId         = 0;
            uint             sequenceNumber    = 0;

            ArraySegment <byte> messageBody;

            try {
                messageBody = ReadAsymmetricMessage(
                    messageChunk,
                    ServerCertificate,
                    out channelId,
                    out clientCertificate,
                    out requestId,
                    out sequenceNumber);

                // check for replay attacks.
                if (!VerifySequenceNumber(sequenceNumber, "ProcessOpenSecureChannelRequest"))
                {
                    throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid);
                }
            } catch (Exception e) {
                ServiceResultException innerException = e.InnerException as ServiceResultException;

                // If the certificate structre, signare and trust list checks pass, we return the other specific validation errors instead of BadSecurityChecksFailed

                if (innerException != null && (
                        innerException.StatusCode == StatusCodes.BadCertificateTimeInvalid ||
                        innerException.StatusCode == StatusCodes.BadCertificateIssuerTimeInvalid ||
                        innerException.StatusCode == StatusCodes.BadCertificateHostNameInvalid ||
                        innerException.StatusCode == StatusCodes.BadCertificateUriInvalid ||
                        innerException.StatusCode == StatusCodes.BadCertificateUseNotAllowed ||
                        innerException.StatusCode == StatusCodes.BadCertificateIssuerUseNotAllowed ||
                        innerException.StatusCode == StatusCodes.BadCertificateRevocationUnknown ||
                        innerException.StatusCode == StatusCodes.BadCertificateIssuerRevocationUnknown ||
                        innerException.StatusCode == StatusCodes.BadCertificateRevoked ||
                        innerException.StatusCode == StatusCodes.BadCertificateIssuerRevoked))
                {
                    ForceChannelFault(innerException, innerException.StatusCode, e.Message);
                    return(false);
                }
                else
                {
                    ForceChannelFault(e, StatusCodes.BadSecurityChecksFailed,
                                      "Could not verify security on OpenSecureChannel request.");
                    return(false);
                }
            }

            BufferCollection chunksToProcess = null;

            try {
                bool firstCall = ClientCertificate == null;

                // must ensure the same certificate was used.
                if (ClientCertificate != null)
                {
                    CompareCertificates(ClientCertificate, clientCertificate, false);
                }
                else
                {
                    ClientCertificate = clientCertificate;
                }

                // check if it is necessary to wait for more chunks.
                if (!TcpMessageType.IsFinal(messageType))
                {
                    SaveIntermediateChunk(requestId, messageBody);
                    return(false);
                }

                // create a new token.
                TcpChannelToken token = CreateToken();

                token.TokenId     = GetNewTokenId();
                token.ServerNonce = CreateNonce();

                // get the chunks to process.
                chunksToProcess = GetSavedChunks(requestId, messageBody);

                OpenSecureChannelRequest request = (OpenSecureChannelRequest)BinaryDecoder.DecodeMessage(
                    new ArraySegmentStream(chunksToProcess),
                    typeof(OpenSecureChannelRequest),
                    Quotas.MessageContext);

                if (request == null)
                {
                    throw ServiceResultException.Create(StatusCodes.BadStructureMissing,
                                                        "Could not parse OpenSecureChannel request body.");
                }

                // check the security mode.
                if (request.SecurityMode != SecurityMode)
                {
                    ReviseSecurityMode(firstCall, request.SecurityMode);
                }

                // check the client nonce.
                token.ClientNonce = request.ClientNonce;

                if (!ValidateNonce(token.ClientNonce))
                {
                    throw ServiceResultException.Create(StatusCodes.BadNonceInvalid,
                                                        "Client nonce is not the correct length or not random enough.");
                }

                // choose the lifetime.
                int lifetime = (int)request.RequestedLifetime;

                if (lifetime < TcpMessageLimits.MinSecurityTokenLifeTime)
                {
                    lifetime = TcpMessageLimits.MinSecurityTokenLifeTime;
                }

                if (lifetime > 0 && lifetime < token.Lifetime)
                {
                    token.Lifetime = lifetime;
                }

                // check the request type.
                SecurityTokenRequestType requestType = request.RequestType;

                if (requestType == SecurityTokenRequestType.Issue && State != TcpChannelState.Opening)
                {
                    throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid,
                                                        "Cannot request a new token for an open channel.");
                }

                if (requestType == SecurityTokenRequestType.Renew && State != TcpChannelState.Open)
                {
                    // may be reconnecting to a dropped channel.
                    if (State == TcpChannelState.Opening)
                    {
                        // tell the listener to find the channel that can process the request.
                        m_listener.ReconnectToExistingChannel(
                            Socket,
                            requestId,
                            sequenceNumber,
                            channelId,
                            ClientCertificate,
                            token,
                            request);

                        Utils.Trace(
                            "TCPSERVERCHANNEL ReconnectToExistingChannel Socket={0:X8}, ChannelId={1}, TokenId={2}",
                            (Socket != null) ? Socket.Handle : 0,
                            (CurrentToken != null) ? CurrentToken.ChannelId : 0,
                            (CurrentToken != null) ? CurrentToken.TokenId : 0);

                        // close the channel.
                        ChannelClosed();

                        // nothing more to do.
                        return(false);
                    }

                    throw ServiceResultException.Create(StatusCodes.BadRequestTypeInvalid,
                                                        "Cannot request to rewew a token for a channel that has not been opened.");
                }

                // check the channel id.
                if (requestType == SecurityTokenRequestType.Renew && channelId != ChannelId)
                {
                    throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown,
                                                        "Do not recognize the secure channel id provided.");
                }

                // log security information.
                if (requestType == SecurityTokenRequestType.Issue)
                {
                    Opc.Ua.Security.Audit.SecureChannelCreated(
                        g_ImplementationString,
                        this.m_listener.EndpointUrl.ToString(),
                        Utils.Format("{0}", this.ChannelId),
                        this.EndpointDescription,
                        this.ClientCertificate,
                        this.ServerCertificate,
                        BinaryEncodingSupport.Required);
                }
                else
                {
                    Opc.Ua.Security.Audit.SecureChannelRenewed(
                        g_ImplementationString,
                        Utils.Format("{0}", this.ChannelId));
                }

                if (requestType == SecurityTokenRequestType.Renew)
                {
                    SetRenewedToken(token);
                }
                else
                {
                    ActivateToken(token);
                }

                State = TcpChannelState.Open;

                // send the response.
                SendOpenSecureChannelResponse(requestId, token, request);
                return(false);
            } catch (Exception e) {
                SendServiceFault(requestId,
                                 ServiceResult.Create(e, StatusCodes.BadTcpInternalError,
                                                      "Unexpected error processing OpenSecureChannel request."));
                return(false);
            } finally {
                if (chunksToProcess != null)
                {
                    chunksToProcess.Release(BufferManager, "ProcessOpenSecureChannelRequest");
                }
            }
        }
        /// <summary>
        /// Computes the keys for a token.
        /// </summary>
        protected void ComputeKeys(TcpChannelToken token)
        {        
            if (SecurityMode == MessageSecurityMode.None)
            {
                return;
            }

            if (SecurityPolicyUri == SecurityPolicies.Basic256Sha256)
            {
                token.ClientSigningKey = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, 0, m_signatureKeySize);
                token.ClientEncryptingKey = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize, m_encryptionKeySize);
                token.ClientInitializationVector = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
                token.ServerSigningKey = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, 0, m_signatureKeySize);
                token.ServerEncryptingKey = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize, m_encryptionKeySize);
                token.ServerInitializationVector = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
            }
            else
            {
                token.ClientSigningKey = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, 0, m_signatureKeySize);
                token.ClientEncryptingKey = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize, m_encryptionKeySize);
                token.ClientInitializationVector = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
                token.ServerSigningKey = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, 0, m_signatureKeySize);
                token.ServerEncryptingKey = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize, m_encryptionKeySize);
                token.ServerInitializationVector = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
            }

            switch (SecurityPolicyUri)
            {
                case SecurityPolicies.Basic128Rsa15:
                case SecurityPolicies.Basic256:
                case SecurityPolicies.Basic256Sha256:
                    {
                        // create encryptors. 
                        SymmetricAlgorithm AesCbcEncryptorProvider = Aes.Create();
                        AesCbcEncryptorProvider.Mode = CipherMode.CBC;
                        AesCbcEncryptorProvider.Padding = PaddingMode.None;
                        AesCbcEncryptorProvider.Key = token.ClientEncryptingKey;
                        AesCbcEncryptorProvider.IV = token.ClientInitializationVector;
                        token.ClientEncryptor = AesCbcEncryptorProvider;

                        SymmetricAlgorithm AesCbcDecryptorProvider = Aes.Create();
                        AesCbcDecryptorProvider.Mode = CipherMode.CBC;
                        AesCbcDecryptorProvider.Padding = PaddingMode.None;
                        AesCbcDecryptorProvider.Key = token.ServerEncryptingKey;
                        AesCbcDecryptorProvider.IV = token.ServerInitializationVector;
                        token.ServerEncryptor = AesCbcDecryptorProvider;

                        // create HMACs.
                        if (SecurityPolicyUri == SecurityPolicies.Basic256Sha256)
                        {
                            // SHA256
                            token.ServerHmac = new HMACSHA256(token.ServerSigningKey);
                            token.ClientHmac = new HMACSHA256(token.ClientSigningKey);
                        }
                        else
                        {   // SHA1
                            token.ServerHmac = new HMACSHA1(token.ServerSigningKey);
                            token.ClientHmac = new HMACSHA1(token.ClientSigningKey);
                        }
                        break;
                    }

                default:
                case SecurityPolicies.None:             
                    {
                        break;
                    }
            }            
        }
Beispiel #15
0
        /// <summary>
        /// Processes a request message.
        /// </summary>
        private bool ProcessRequestMessage(uint messageType, ArraySegment <byte> messageChunk)
        {
            // validate the channel state.
            if (State != TcpChannelState.Open)
            {
                ForceChannelFault(StatusCodes.BadTcpMessageTypeInvalid, "Client sent an unexpected request message.");
                return(false);
            }

            // validate security on the message.
            TcpChannelToken token          = null;
            uint            requestId      = 0;
            uint            sequenceNumber = 0;

            ArraySegment <byte> messageBody;

            try {
                messageBody = ReadSymmetricMessage(messageChunk, true, out token, out requestId, out sequenceNumber);

                // check for replay attacks.
                if (!VerifySequenceNumber(sequenceNumber, "ProcessRequestMessage"))
                {
                    throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid);
                }

                if (token == CurrentToken && PreviousToken != null && !PreviousToken.Expired)
                {
                    Utils.Trace("Server Revoked Token. ChannelId={1}, TokenId={0}", PreviousToken.TokenId,
                                PreviousToken.ChannelId, DateTime.UtcNow);
                    PreviousToken.Lifetime = 0;
                }
            } catch (Exception e) {
                Utils.Trace("Could not verify security on incoming request.");
                ForceChannelFault(e, StatusCodes.BadSecurityChecksFailed,
                                  "Could not verify security on incoming request.");
                return(false);
            }

            BufferCollection chunksToProcess = null;

            try {
                // check for an abort.
                if (TcpMessageType.IsAbort(messageType))
                {
                    Utils.Trace("Request was aborted.");
                    chunksToProcess = GetSavedChunks(requestId, messageBody);
                    return(true);
                }

                // check if it is necessary to wait for more chunks.
                if (!TcpMessageType.IsFinal(messageType))
                {
                    SaveIntermediateChunk(requestId, messageBody);
                    return(true);
                }

                // Utils.Trace("Channel {0}: ProcessRequestMessage {1}", ChannelId, requestId);

                // get the chunks to process.
                chunksToProcess = GetSavedChunks(requestId, messageBody);

                // decode the request.
                IServiceRequest request =
                    BinaryDecoder.DecodeMessage(new ArraySegmentStream(chunksToProcess), null, Quotas.MessageContext) as
                    IServiceRequest;

                if (request == null)
                {
                    SendServiceFault(token, requestId,
                                     ServiceResult.Create(StatusCodes.BadStructureMissing, "Could not parse request body."));
                    return(true);
                }

                // ensure that only discovery requests come through unsecured.
                if (DiscoveryOnly)
                {
                    if (!(request is GetEndpointsRequest || request is FindServersRequest))
                    {
                        SendServiceFault(token, requestId,
                                         ServiceResult.Create(StatusCodes.BadSecurityPolicyRejected,
                                                              "Channel can only be used for discovery."));
                        return(true);
                    }
                }

                // hand the request to the server.
                if (m_RequestReceived != null)
                {
                    m_RequestReceived(this, requestId, request);
                }

                return(true);
            } catch (Exception e) {
                Utils.Trace(e, "Unexpected error processing request.");
                SendServiceFault(token, requestId,
                                 ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request."));
                return(false);
            } finally {
                if (chunksToProcess != null)
                {
                    chunksToProcess.Release(BufferManager, "ProcessRequestMessage");
                }
            }
        }
Beispiel #16
0
        /// <summary>
        /// Processes an CloseSecureChannel request message.
        /// </summary>
        private bool ProcessCloseSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk)
        {
            // validate security on the message.
            TcpChannelToken token          = null;
            uint            requestId      = 0;
            uint            sequenceNumber = 0;

            ArraySegment <byte> messageBody;

            try {
                messageBody = ReadSymmetricMessage(messageChunk, true, out token, out requestId, out sequenceNumber);

                // check for replay attacks.
                if (!VerifySequenceNumber(sequenceNumber, "ProcessCloseSecureChannelRequest"))
                {
                    throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid);
                }
            } catch (Exception e) {
                throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, e,
                                                    "Could not verify security on CloseSecureChannel request.");
            }

            BufferCollection chunksToProcess = null;

            try {
                // check if it is necessary to wait for more chunks.
                if (!TcpMessageType.IsFinal(messageType))
                {
                    SaveIntermediateChunk(requestId, messageBody);
                    return(false);
                }

                // get the chunks to process.
                chunksToProcess = GetSavedChunks(requestId, messageBody);

                CloseSecureChannelRequest request = BinaryDecoder.DecodeMessage(
                    new ArraySegmentStream(chunksToProcess),
                    typeof(CloseSecureChannelRequest),
                    Quotas.MessageContext) as CloseSecureChannelRequest;

                if (request == null)
                {
                    throw ServiceResultException.Create(StatusCodes.BadStructureMissing,
                                                        "Could not parse CloseSecureChannel request body.");
                }

                // send the response.
                // SendCloseSecureChannelResponse(requestId, token, request);
            } catch (Exception e) {
                Utils.Trace(e, "Unexpected error processing OpenSecureChannel request.");
            } finally {
                if (chunksToProcess != null)
                {
                    chunksToProcess.Release(BufferManager, "ProcessCloseSecureChannelRequest");
                }

                Utils.Trace(
                    "TCPSERVERCHANNEL ProcessCloseSecureChannelRequest Socket={0:X8}, ChannelId={1}, TokenId={2}",
                    (Socket != null) ? Socket.Handle : 0,
                    (CurrentToken != null) ? CurrentToken.ChannelId : 0,
                    (CurrentToken != null) ? CurrentToken.TokenId : 0);

                // close the channel.
                ChannelClosed();
            }

            return(false);
        }
Beispiel #17
0
        protected ArraySegment <byte> ReadSymmetricMessage(
            ArraySegment <byte> buffer,
            bool isRequest,
            out TcpChannelToken token,
            out uint requestId,
            out uint sequenceNumber)
        {
            BinaryDecoder decoder = new BinaryDecoder(buffer.Array, buffer.Offset, buffer.Count, Quotas.MessageContext);

            uint messageType = decoder.ReadUInt32(null);
            uint messageSize = decoder.ReadUInt32(null);
            uint channelId   = decoder.ReadUInt32(null);
            uint tokenId     = decoder.ReadUInt32(null);

            // ensure the channel is valid.
            if (channelId != ChannelId)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadTcpSecureChannelUnknown,
                          "SecureChannelId is not known. ChanneId={0}, CurrentChannelId={1}",
                          channelId,
                          ChannelId);
            }

            // check for valid token.
            TcpChannelToken currentToken = CurrentToken;

            if (currentToken == null)
            {
                throw new ServiceResultException(StatusCodes.BadSecureChannelClosed);
            }

            // find the token.
            if (currentToken.TokenId != tokenId && PreviousToken != null && PreviousToken.TokenId != tokenId)
            {
                throw ServiceResultException.Create(
                          StatusCodes.BadTcpSecureChannelUnknown,
                          "TokenId is not known. ChanneId={0}, TokenId={1}, CurrentTokenId={2}, PreviousTokenId={3}",
                          channelId,
                          tokenId,
                          currentToken.TokenId,
                          (PreviousToken != null)?(int)PreviousToken.TokenId:-1);
            }

            token = currentToken;

            // check for a message secured with the token before it expired.
            if (PreviousToken != null && PreviousToken.TokenId == tokenId)
            {
                token = PreviousToken;
            }

            // check if token has expired.
            if (token.Expired)
            {
                throw ServiceResultException.Create(StatusCodes.BadTcpSecureChannelUnknown, "Token #{0} has expired. Lifetime={1:HH:mm:ss.fff}", token.TokenId, token.CreatedAt);
            }

            int headerSize = decoder.Position;

            if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
            {
                // decrypt the message.
                Decrypt(token, new ArraySegment <byte>(buffer.Array, buffer.Offset + headerSize, buffer.Count - headerSize), isRequest);
            }

            if (SecurityMode != MessageSecurityMode.None)
            {
                // extract signature.
                byte[] signature = new byte[SymmetricSignatureSize];

                for (int ii = 0; ii < SymmetricSignatureSize; ii++)
                {
                    signature[ii] = buffer.Array[buffer.Offset + buffer.Count - SymmetricSignatureSize + ii];
                }

                // verify the signature.
                if (!Verify(token, signature, new ArraySegment <byte>(buffer.Array, buffer.Offset, buffer.Count - SymmetricSignatureSize), isRequest))
                {
                    Utils.Trace("Could not verify signature on message.");
                    throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the signature on the message.");
                }
            }

            int paddingCount = 0;

            if (SecurityMode == MessageSecurityMode.SignAndEncrypt)
            {
                // verify padding.
                int paddingStart = buffer.Offset + buffer.Count - SymmetricSignatureSize - 1;
                paddingCount = buffer.Array[paddingStart];

                for (int ii = paddingStart - paddingCount; ii < paddingStart; ii++)
                {
                    if (buffer.Array[ii] != paddingCount)
                    {
                        throw ServiceResultException.Create(StatusCodes.BadSecurityChecksFailed, "Could not verify the padding in the message.");
                    }
                }

                // add byte for size.
                paddingCount++;
            }

            // extract request id and sequence number.
            sequenceNumber = decoder.ReadUInt32(null);
            requestId      = decoder.ReadUInt32(null);

            // return an the data contained in the message.
            int startOfBody = buffer.Offset + TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize;
            int sizeOfBody  = buffer.Count - TcpMessageLimits.SymmetricHeaderSize - TcpMessageLimits.SequenceHeaderSize - paddingCount - SymmetricSignatureSize;

            return(new ArraySegment <byte>(buffer.Array, startOfBody, sizeOfBody));
        }
        /// <summary>
        /// Signs the message using SHA1 HMAC
        /// </summary>
        private static byte[] SymmetricSign(TcpChannelToken token, ArraySegment<byte> dataToSign, bool useClientKeys)
        {     
            // get HMAC object.
            HMAC hmac = (useClientKeys)?token.ClientHmac:token.ServerHmac;

            // compute hash.
            MemoryStream istrm = new MemoryStream(dataToSign.Array, dataToSign.Offset, dataToSign.Count, false);
            byte[] signature = hmac.ComputeHash(istrm);
            istrm.Close();
                                   
            // return signature.
            return signature;
        }
        /// <summary>
        /// Sends a fault response secured with the symmetric keys.
        /// </summary>
        private void SendServiceFault(TcpChannelToken token, uint requestId, ServiceResult fault)
        {   
            // Utils.Trace("Channel {0} Request {1}: SendServiceFault()", ChannelId, requestId);
            
            BufferCollection buffers = null;

            try
            {
                // construct fault.
                ServiceFault response = new ServiceFault();
                
                response.ResponseHeader.ServiceResult = fault.Code;

                StringTable stringTable = new StringTable();

                response.ResponseHeader.ServiceDiagnostics = new DiagnosticInfo(
                    fault, 
                    DiagnosticsMasks.NoInnerStatus, 
                    true, 
                    stringTable);

                response.ResponseHeader.StringTable = stringTable.ToArray();
                
                // the limits should never be exceeded when sending a fault.
                bool limitsExceeded = false;

                // secure message.
                buffers = WriteSymmetricMessage(
                    TcpMessageType.Message,
                    requestId,
                    token,
                    response,
                    false,
                    out limitsExceeded);

                // send message.
                BeginWriteMessage(buffers, Int32.MaxValue, null);
                buffers = null;
            }
            catch (Exception e)
            {
                if (buffers != null)
                {
                    buffers.Release(BufferManager, "SendServiceFault");
                }

                ForceChannelFault(ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error sending a service fault."));
            }
        }
        /// <summary>
        /// Verifies a HMAC for a message.
        /// </summary>
        private static bool SymmetricVerify(
            TcpChannelToken    token, 
            byte[]             signature,
            ArraySegment<byte> dataToVerify,
            bool               useClientKeys)
        {
            // get HMAC object.
            HMAC hmac = (useClientKeys)?token.ClientHmac:token.ServerHmac;
                                    
            // compute hash.
            MemoryStream istrm = new MemoryStream(dataToVerify.Array, dataToVerify.Offset, dataToVerify.Count, false);
            byte[] computedSignature = hmac.ComputeHash(istrm);
            istrm.Close();

            // compare signatures.
            for (int ii = 0; ii < signature.Length; ii++)
            {
                if (computedSignature[ii] != signature[ii])
                {
                    string messageType = new UTF8Encoding().GetString(dataToVerify.Array, dataToVerify.Offset, 4);
                    int messageLength = BitConverter.ToInt32(dataToVerify.Array, dataToVerify.Offset+4);
                    string expectedSignature = Utils.ToHexString(computedSignature);
                    string actualSignature = Utils.ToHexString(signature);

                    Utils.Trace(
                        "Could not validate signature.\r\nChannelId={0}, TokenId={1}, MessageType={2}, Length={3}\r\nExpectedSignature={4}\r\nActualSignature  ={5}",
                        token.ChannelId,                        
                        token.TokenId,
                        messageType,
                        messageLength,
                        expectedSignature,
                        actualSignature);

                    return false;
                }
            }

            return true;
        }
        /// <summary>
        /// Computes the keys for a token.
        /// </summary>
        protected void ComputeKeys(TcpChannelToken token)
        {
            if (SecurityMode == MessageSecurityMode.None)
            {
                return;
            }

            if (SecurityPolicyUri == SecurityPolicies.Basic256Sha256)
            {
                token.ClientSigningKey           = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, 0, m_signatureKeySize);
                token.ClientEncryptingKey        = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize, m_encryptionKeySize);
                token.ClientInitializationVector = Utils.PSHA256(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
                token.ServerSigningKey           = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, 0, m_signatureKeySize);
                token.ServerEncryptingKey        = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize, m_encryptionKeySize);
                token.ServerInitializationVector = Utils.PSHA256(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
            }
            else
            {
                token.ClientSigningKey           = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, 0, m_signatureKeySize);
                token.ClientEncryptingKey        = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize, m_encryptionKeySize);
                token.ClientInitializationVector = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
                token.ServerSigningKey           = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, 0, m_signatureKeySize);
                token.ServerEncryptingKey        = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize, m_encryptionKeySize);
                token.ServerInitializationVector = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
            }

            switch (SecurityPolicyUri)
            {
            case SecurityPolicies.Basic128Rsa15:
            case SecurityPolicies.Basic256:
            {
                // create encryptors.
                RijndaelManaged encryptor = new RijndaelManaged();

                encryptor.Key     = token.ClientEncryptingKey;
                encryptor.IV      = token.ClientInitializationVector;
                encryptor.Mode    = CipherMode.CBC;
                encryptor.Padding = PaddingMode.None;

                token.ClientEncryptor = encryptor;

                encryptor = new RijndaelManaged();

                encryptor.Key     = token.ServerEncryptingKey;
                encryptor.IV      = token.ServerInitializationVector;
                encryptor.Mode    = CipherMode.CBC;
                encryptor.Padding = PaddingMode.None;

                token.ServerEncryptor = encryptor;

                // create HMACs.
                token.ServerHmac = new HMACSHA1(token.ServerSigningKey);
                token.ClientHmac = new HMACSHA1(token.ClientSigningKey);
                break;
            }

            case SecurityPolicies.Basic256Sha256:
            {
                // create encryptors.
                RijndaelManaged encryptor = new RijndaelManaged();

                encryptor.Key     = token.ClientEncryptingKey;
                encryptor.IV      = token.ClientInitializationVector;
                encryptor.Mode    = CipherMode.CBC;
                encryptor.Padding = PaddingMode.None;

                token.ClientEncryptor = encryptor;

                encryptor = new RijndaelManaged();

                encryptor.Key     = token.ServerEncryptingKey;
                encryptor.IV      = token.ServerInitializationVector;
                encryptor.Mode    = CipherMode.CBC;
                encryptor.Padding = PaddingMode.None;

                token.ServerEncryptor = encryptor;

                // create HMACs.
                token.ServerHmac = new HMACSHA256(token.ServerSigningKey);
                token.ClientHmac = new HMACSHA256(token.ClientSigningKey);
                break;
            }

            case SecurityPolicies.None:
            default:
            {
                break;
            }
            }
        }
Beispiel #22
0
        /// <summary>
        /// Schedules the renewal of a token.
        /// </summary>
        private void ScheduleTokenRenewal(TcpChannelToken token)
        {
            // can't renew if not connected.
            if (State != TcpChannelState.Open)
            {
                return;
            }

            // cancel any outstanding renew operations.
            if (m_handshakeTimer != null)
            {
                m_handshakeTimer.Dispose();
                m_handshakeTimer = null;
            }

            // calculate renewal timing based on token lifetime.
            DateTime expiryTime = token.CreatedAt.AddMilliseconds(token.Lifetime);
            
            double timeToRenewal = ((expiryTime.Ticks - DateTime.UtcNow.Ticks)/TimeSpan.TicksPerMillisecond)*TcpMessageLimits.TokenRenewalPeriod;

            if (timeToRenewal < 0)
            {
                timeToRenewal = 0;
            }          
            
            m_handshakeTimer = new Timer(m_StartHandshake, token, (int)timeToRenewal, Timeout.Infinite);
        }
        /// <summary>
        /// Sets the renewed token
        /// </summary>
        protected void SetRenewedToken(TcpChannelToken token)
        {
            m_renewedToken = token;

            Utils.Trace("RenewedToken #{0} set. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime);
        }
        /// <summary>
        /// Handles a reconnect request.
        /// </summary>
        public void Reconnect(
            TcpMessageSocket         socket, 
            uint                     requestId,
            uint                     sequenceNumber,
            X509Certificate2         clientCertificate, 
            TcpChannelToken          token,
            OpenSecureChannelRequest request)
        {      
            if (socket == null) throw new ArgumentNullException("socket");
            
            lock (DataLock)
            {
                // make sure the same client certificate is being used.     
                CompareCertificates(ClientCertificate, clientCertificate, false);

                // check for replay attacks.
                if (!VerifySequenceNumber(sequenceNumber, "Reconnect"))
                {
                    throw new ServiceResultException(StatusCodes.BadSequenceNumberInvalid);
                }

                try
                {        
                    // replace the socket.
                    Socket = socket;
                    Utils.Trace("TCPSERVERCHANNEL SOCKET RECONNECTED: {0:X8}, ChannelId={1}", Socket.Handle, ChannelId);
                    Socket.ChangeSink(this);

                    // need to assign a new token id.
                    token.TokenId = GetNewTokenId();

                    // put channel back in open state.
                    ActivateToken(token);
                    State = TcpChannelState.Open;

                    // no need to cleanup.
                    if (m_cleanupTimer != null)
                    {
                        m_cleanupTimer.Dispose();
                        m_cleanupTimer = null;
                    }

                    // send response.
                    SendOpenSecureChannelResponse(requestId, token, request);

                    // send any queue responses.
                    ThreadPool.QueueUserWorkItem(new WaitCallback(OnChannelReconnected), m_queuedResponses);
                    m_queuedResponses = new SortedDictionary<uint,IServiceResponse>();
                }
                catch (Exception e)
                {
                    SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request."));
                }
            }
        }
        /// <summary>
        /// Computes the keys for a token.
        /// </summary>
        protected void ComputeKeys(TcpChannelToken token)
        {        
            if (SecurityMode == MessageSecurityMode.None)
            {
                return;
            }
            
            token.ClientSigningKey           = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, 0, m_signatureKeySize);
            token.ClientEncryptingKey        = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize, m_encryptionKeySize);
            token.ClientInitializationVector = Utils.PSHA1(token.ServerNonce, null, token.ClientNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);
            token.ServerSigningKey           = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, 0, m_signatureKeySize);
            token.ServerEncryptingKey        = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize, m_encryptionKeySize);
            token.ServerInitializationVector = Utils.PSHA1(token.ClientNonce, null, token.ServerNonce, m_signatureKeySize + m_encryptionKeySize, m_encryptionBlockSize);

            switch (SecurityPolicyUri)
            {
                case SecurityPolicies.Basic128Rsa15:
                case SecurityPolicies.Basic256:
                {
                    // create encryptors.                    
                    RijndaelManaged encryptor = new RijndaelManaged();

                    encryptor.Key     = token.ClientEncryptingKey;
                    encryptor.IV      = token.ClientInitializationVector;
                    encryptor.Mode    = CipherMode.CBC;
                    encryptor.Padding = PaddingMode.None;
                    
                    token.ClientEncryptor = encryptor;
                    
                    encryptor = new RijndaelManaged();

                    encryptor.Key     = token.ServerEncryptingKey;
                    encryptor.IV      = token.ServerInitializationVector;
                    encryptor.Mode    = CipherMode.CBC;
                    encryptor.Padding = PaddingMode.None;
                    
                    token.ServerEncryptor = encryptor;

                    // create HMACs.
                    token.ServerHmac = new HMACSHA1(token.ServerSigningKey);
                    token.ClientHmac = new HMACSHA1(token.ClientSigningKey);
                    break;
                }

                default:
                case SecurityPolicies.None:             
                {
                    break;
                }
            }            
        }
        /// <summary>
        /// Sends an OpenSecureChannel response.
        /// </summary>
        private void SendOpenSecureChannelResponse(uint requestId, TcpChannelToken token, OpenSecureChannelRequest request)
        {
            // Utils.Trace("Channel {0}: SendOpenSecureChannelResponse()", ChannelId);

            OpenSecureChannelResponse response = new OpenSecureChannelResponse();
            
            response.ResponseHeader.RequestHandle = request.RequestHeader.RequestHandle;
            response.ResponseHeader.Timestamp     = DateTime.UtcNow;

            response.SecurityToken.ChannelId = token.ChannelId;
            response.SecurityToken.TokenId = token.TokenId;
            response.SecurityToken.CreatedAt = token.CreatedAt;
            response.SecurityToken.RevisedLifetime = (uint)token.Lifetime;
            response.ServerNonce = token.ServerNonce;
            
            byte[] buffer = BinaryEncoder.EncodeMessage(response, Quotas.MessageContext); 
            
            BufferCollection chunksToSend = WriteAsymmetricMessage(
                TcpMessageType.Open,
                requestId,
                //ServerCertificateChain,
                ServerCertificate,
                ClientCertificate,
                new ArraySegment<byte>(buffer, 0, buffer.Length));

            // write the message to the server.
            try
            {
                BeginWriteMessage(chunksToSend, Int32.MaxValue, null);
                chunksToSend = null;
            }
            finally
            {
                if (chunksToSend != null)
                {
                    chunksToSend.Release(BufferManager, "SendOpenSecureChannelResponse");
                }
            }
        }      
        /// <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");
                    }
                }
            }
        }