Esempio n. 1
0
        /// <summary>
        /// Decrypts a message using a symmetric algorithm.
        /// </summary>
        private static void SymmetricDecrypt(
            ChannelToken token,
            ArraySegment <byte> dataToDecrypt,
            bool useClientKeys)
        {
            // get the decrypting key.
            SymmetricAlgorithm decryptingKey = (useClientKeys) ? token.ClientEncryptor : token.ServerEncryptor;

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

            using (ICryptoTransform decryptor = decryptingKey.CreateDecryptor())
            {
                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);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Returns the symmetric signature for the data.
        /// </summary>
        protected bool Verify(
            ChannelToken token,
            byte[] signature,
            ArraySegment <byte> dataToVerify,
            bool useClientKeys)
        {
            // verify signature.
            switch (SecurityPolicyUri)
            {
            case SecurityPolicies.None:
            {
                return(true);
            }

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

            default:
            {
                return(false);
            }
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Activates a new token.
        /// </summary>
        protected void ActivateToken(ChannelToken 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);
        }
Esempio n. 4
0
        /// <summary>
        /// Handles a reconnect request.
        /// </summary>
        public void Reconnect(
            IMessageSocket socket,
            uint requestId,
            uint sequenceNumber,
            X509Certificate2 clientCertificate,
            ChannelToken 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.
                    CleanupTimer();

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

                    // send any queue responses.
                    Task.Factory.StartNew(OnChannelReconnected, m_queuedResponses);
                    m_queuedResponses = new SortedDictionary <uint, IServiceResponse>();
                }
                catch (Exception e)
                {
                    SendServiceFault(token, requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing request."));
                }
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Activates a new token.
        /// </summary>
        protected void ActivateToken(ChannelToken token)
        {
            // compute the keys for the token.
            ComputeKeys(token);

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

            Utils.LogInfo("ChannelId {0}: Token #{1} activated. CreatedAt={2:HH:mm:ss.fff}. Lifetime={3}.", Id, token.TokenId, token.CreatedAt, token.Lifetime);
        }
 /// <summary>
 /// Binds a new socket to an existing channel.
 /// </summary>
 public bool ReconnectToExistingChannel(
     IMessageSocket socket,
     uint requestId,
     uint sequenceNumber,
     uint channelId,
     X509Certificate2 clientCertificate,
     ChannelToken token,
     OpenSecureChannelRequest request)
 {
     return(true);
 }
Esempio n. 7
0
        /// <summary>
        /// Creates a new token.
        /// </summary>
        protected ChannelToken CreateToken()
        {
            ChannelToken token = new ChannelToken();

            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);
        }
Esempio n. 8
0
        /// <summary>
        /// Creates a new token.
        /// </summary>
        protected ChannelToken CreateToken()
        {
            ChannelToken token = new ChannelToken();

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

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

            return(token);
        }
Esempio n. 9
0
        /// <summary>
        /// Signs the message using SHA1 HMAC
        /// </summary>
        private static byte[] SymmetricSign(ChannelToken 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.Dispose();

            // return signature.
            return(signature);
        }
Esempio n. 10
0
        /// <summary>
        /// Sends a fault response secured with the symmetric keys.
        /// </summary>
        protected void SendServiceFault(ChannelToken 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, 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>
        /// Returns the symmetric signature for the data.
        /// </summary>
        protected byte[] Sign(ChannelToken token, ArraySegment <byte> dataToSign, bool useClientKeys)
        {
            switch (SecurityPolicyUri)
            {
            default:
            case SecurityPolicies.None:
            {
                return(null);
            }

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

            case SecurityPolicies.Basic256:
            case SecurityPolicies.Basic256Sha256:
            case SecurityPolicies.Basic128Rsa15:
            {
                SymmetricDecrypt(token, dataToDecrypt, useClientKeys);
                break;
            }
            }
        }
        /// <summary>
        /// Verifies a HMAC for a message.
        /// </summary>
        private static bool SymmetricVerify(
            ChannelToken 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.Dispose();

            // 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>
        /// Sends an OpenSecureChannel response.
        /// </summary>
        private void SendOpenSecureChannelResponse(uint requestId, ChannelToken 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,
                ServerCertificate,
                ServerCertificateChain,
                ClientCertificate,
                new ArraySegment <byte>(buffer, 0, buffer.Length));

            // write the message to the server.
            try
            {
                BeginWriteMessage(chunksToSend, null);
                chunksToSend = null;
            }
            finally
            {
                if (chunksToSend != null)
                {
                    chunksToSend.Release(BufferManager, "SendOpenSecureChannelResponse");
                }
            }
        }
Esempio n. 15
0
        /// <summary>
        /// Binds a new socket to an existing channel.
        /// </summary>
        public bool ReconnectToExistingChannel(
            IMessageSocket socket,
            uint requestId,
            uint sequenceNumber,
            uint channelId,
            X509Certificate2 clientCertificate,
            ChannelToken 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);
        }
Esempio n. 16
0
        /// <summary>
        /// Sets the renewed token
        /// </summary>
        protected void SetRenewedToken(ChannelToken token)
        {
            m_renewedToken = token;

            Utils.Trace("RenewedToken #{0} set. CreatedAt = {1:HH:mm:ss.fff} . Lifetime = {2}", token.TokenId, token.CreatedAt, token.Lifetime);
        }
Esempio n. 17
0
 /// <summary>
 /// Sets the renewed token
 /// </summary>
 protected void SetRenewedToken(ChannelToken token)
 {
     m_renewedToken = token;
     Utils.LogInfo("ChannelId {0}: Renewed Token #{1} set. CreatedAt={2:HH:mm:ss.fff}. Lifetime ={3}.", Id, token.TokenId, token.CreatedAt, token.Lifetime);
 }
        /// <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, signature and trust list checks pass, we return the other specific validation errors instead of BadSecurityChecksFailed
                if (innerException != null)
                {
                    if (innerException.StatusCode == StatusCodes.BadCertificateUntrusted ||
                        innerException.StatusCode == StatusCodes.BadCertificateChainIncomplete ||
                        innerException.StatusCode == StatusCodes.BadCertificateRevoked ||
                        innerException.StatusCode == StatusCodes.BadCertificateInvalid ||
                        (innerException.InnerResult != null && innerException.InnerResult.StatusCode == StatusCodes.BadCertificateUntrusted))
                    {
                        ForceChannelFault(StatusCodes.BadSecurityChecksFailed, e.Message);
                        return(false);
                    }
                    else if (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.BadCertificateIssuerRevoked)
                    {
                        ForceChannelFault(innerException, innerException.StatusCode, e.Message);
                        return(false);
                    }
                }

                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.
                ChannelToken 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.
                        Listener.ReconnectToExistingChannel(
                            Socket,
                            requestId,
                            sequenceNumber,
                            channelId,
                            ClientCertificate,
                            token,
                            request);

                        Utils.Trace(
                            "{0} ReconnectToExistingChannel Socket={0:X8}, ChannelId={1}, TokenId={2}",
                            ChannelName,
                            (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 renew 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(
                        m_ImplementationString,
                        Listener.EndpointUrl.ToString(),
                        Utils.Format("{0}", ChannelId),
                        EndpointDescription,
                        ClientCertificate,
                        ServerCertificate,
                        BinaryEncodingSupport.Required);
                }
                else
                {
                    Opc.Ua.Security.Audit.SecureChannelRenewed(
                        m_ImplementationString,
                        Utils.Format("{0}", ChannelId));
                }

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

                State = TcpChannelState.Open;

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

                // notify reverse
                CompleteReverseHello(null);

                // notify any monitors.
                NotifyMonitors(ServiceResult.Good, false);

                return(false);
            }
            catch (Exception e)
            {
                SendServiceFault(requestId, ServiceResult.Create(e, StatusCodes.BadTcpInternalError, "Unexpected error processing OpenSecureChannel request."));
                CompleteReverseHello(e);
                return(false);
            }
            finally
            {
                if (chunksToProcess != null)
                {
                    chunksToProcess.Release(BufferManager, "ProcessOpenSecureChannelRequest");
                }
            }
        }
Esempio n. 19
0
        protected ArraySegment <byte> ReadSymmetricMessage(
            ArraySegment <byte> buffer,
            bool isRequest,
            out ChannelToken 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.
            ChannelToken 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));
        }
Esempio n. 20
0
        /// <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");
                    }
                }
            }
        }
Esempio n. 21
0
        /// <summary>
        /// Computes the keys for a token.
        /// </summary>
        protected void ComputeKeys(ChannelToken token)
        {
            if (SecurityMode == MessageSecurityMode.None)
            {
                return;
            }

            if (SecurityPolicyUri == SecurityPolicies.Basic256Sha256 ||
                SecurityPolicyUri == SecurityPolicies.Aes128_Sha256_RsaOaep ||
                SecurityPolicyUri == SecurityPolicies.Aes256_Sha256_RsaPss)
            {
                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:
            case SecurityPolicies.Aes128_Sha256_RsaOaep:
            case SecurityPolicies.Aes256_Sha256_RsaPss:
            {
                // 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 ||
                    SecurityPolicyUri == SecurityPolicies.Aes128_Sha256_RsaOaep ||
                    SecurityPolicyUri == SecurityPolicies.Aes256_Sha256_RsaPss)
                {
                    // 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;
            }
            }
        }
Esempio n. 22
0
 /// <summary>
 /// Handles a reconnect request.
 /// </summary>
 public virtual void Reconnect(IMessageSocket socket, uint requestId, uint sequenceNumber, X509Certificate2 clientCertificate, ChannelToken token, OpenSecureChannelRequest request)
 {
     throw new NotImplementedException();
 }
        /// <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.
            ChannelToken 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.
                RequestReceived?.Invoke(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(true);
            }
            finally
            {
                if (chunksToProcess != null)
                {
                    chunksToProcess.Release(BufferManager, "ProcessRequestMessage");
                }
            }
        }
        /// <summary>
        /// Processes an CloseSecureChannel request message.
        /// </summary>
        private bool ProcessCloseSecureChannelRequest(uint messageType, ArraySegment <byte> messageChunk)
        {
            // validate security on the message.
            ChannelToken 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(
                    "{0} ProcessCloseSecureChannelRequest Socket={0:X8}, ChannelId={1}, TokenId={2}",
                    ChannelName,
                    (Socket != null) ? Socket.Handle : 0,
                    (CurrentToken != null) ? CurrentToken.ChannelId : 0,
                    (CurrentToken != null) ? CurrentToken.TokenId : 0);

                // close the channel.
                ChannelClosed();
            }

            // return false would double free the buffer
            return(true);
        }