File: System\ServiceModel\Security\MessageSecurityProtocol.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//----------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
 
namespace System.ServiceModel.Security
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IdentityModel.Policy;
    using System.IdentityModel.Selectors;
    using System.IdentityModel.Tokens;
    using System.Runtime;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Diagnostics;
    using System.ServiceModel.Description;
    using System.ServiceModel.Security.Tokens;
 
    abstract class MessageSecurityProtocol : SecurityProtocol
    {
        readonly MessageSecurityProtocolFactory factory;
        SecurityToken identityVerifiedToken; // verified for the readonly target
 
        protected MessageSecurityProtocol(MessageSecurityProtocolFactory factory, EndpointAddress target, Uri via)
            : base(factory, target, via)
        {
            this.factory = factory;
        }
 
        // Protocols that have more than one active, identity checked
        // token at any time should override this property and return
        // false
        protected virtual bool CacheIdentityCheckResultForToken
        {
            get { return true; }
        }
 
        protected virtual bool DoAutomaticEncryptionMatch
        {
            get { return true; }
        }
 
        protected virtual bool PerformIncomingAndOutgoingMessageExpectationChecks
        {
            get { return true; }
        }
 
        protected bool RequiresIncomingSecurityProcessing(Message message)
        {
            // if we are receiveing a response that has no security that we should accept this AND no security header exists
            // then it is OK to skip the header.
            if (this.factory.ActAsInitiator
              && this.factory.SecurityBindingElement.EnableUnsecuredResponse
              && !this.factory.StandardsManager.SecurityVersion.DoesMessageContainSecurityHeader(message))
                return false;
 
            bool requiresAppSecurity = this.factory.RequireIntegrity || this.factory.RequireConfidentiality || this.factory.DetectReplays;
            return requiresAppSecurity || factory.ExpectSupportingTokens;
        }
 
        protected bool RequiresOutgoingSecurityProcessing
        {
            get
            {
                // If were are the listener, don't apply security if the flag is set
                if (!this.factory.ActAsInitiator && this.factory.SecurityBindingElement.EnableUnsecuredResponse)
                    return false;
 
                bool requiresAppSecurity = this.factory.ApplyIntegrity || this.factory.ApplyConfidentiality || this.factory.AddTimestamp;
                return requiresAppSecurity || factory.ExpectSupportingTokens;
            }
        }
 
        protected MessageSecurityProtocolFactory MessageSecurityProtocolFactory
        {
            get { return this.factory; }
        }
 
        public override IAsyncResult BeginSecureOutgoingMessage(Message message, TimeSpan timeout, AsyncCallback callback, object state)
        {
            try
            {
                this.CommunicationObject.ThrowIfClosedOrNotOpen();
                ValidateOutgoingState(message);
                if (!this.RequiresOutgoingSecurityProcessing && message.Properties.Security == null)
                {
                    return new CompletedAsyncResult<Message>(message, callback, state);
                }
                return BeginSecureOutgoingMessageCore(message, timeout, null, callback, state);
            }
            catch (Exception exception)
            {
                // Always immediately rethrow fatal exceptions.
                if (Fx.IsFatal(exception)) throw;
 
                base.OnSecureOutgoingMessageFailure(message);
                throw;
            }
        }
 
        public override IAsyncResult BeginSecureOutgoingMessage(Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState, AsyncCallback callback, object state)
        {
            try
            {
                this.CommunicationObject.ThrowIfClosedOrNotOpen();
                ValidateOutgoingState(message);
                if (!this.RequiresOutgoingSecurityProcessing && message.Properties.Security == null)
                {
                    return new CompletedAsyncResult<Message>(message, callback, state);
                }
                return BeginSecureOutgoingMessageCore(message, timeout, correlationState, callback, state);
            }
            catch (Exception exception)
            {
                // Always immediately rethrow fatal exceptions.
                if (Fx.IsFatal(exception)) throw;
 
                base.OnSecureOutgoingMessageFailure(message);
                throw;
            }
        }
 
        protected abstract IAsyncResult BeginSecureOutgoingMessageCore(Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState, AsyncCallback callback, object state);
 
        public override void EndSecureOutgoingMessage(IAsyncResult result, out Message message)
        {
            if (result == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
            }
            try
            {
                SecurityProtocolCorrelationState newCorrelationState;
                EndSecureOutgoingMessageCore(result, out message, out newCorrelationState);
                base.OnOutgoingMessageSecured(message);
            }
            catch (Exception exception)
            {
                // Always immediately rethrow fatal exceptions.
                if (Fx.IsFatal(exception)) throw;
 
                base.OnSecureOutgoingMessageFailure(null);
                throw;
            }
        }
 
        public override void EndSecureOutgoingMessage(IAsyncResult result, out Message message, out SecurityProtocolCorrelationState newCorrelationState)
        {
            if (result == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
            }
            try
            {
                EndSecureOutgoingMessageCore(result, out message, out newCorrelationState);
                base.OnOutgoingMessageSecured(message);
            }
            catch (Exception exception)
            {
                // Always immediately rethrow fatal exceptions.
                if (Fx.IsFatal(exception)) throw;
 
                base.OnSecureOutgoingMessageFailure(null);
                throw;
            }
        }
 
        protected abstract void EndSecureOutgoingMessageCore(IAsyncResult result, out Message message, out SecurityProtocolCorrelationState newCorrelationState);
 
        // helper method for attaching the client claims in a symmetric security protocol
        protected void AttachRecipientSecurityProperty(Message message, SecurityToken protectionToken, bool isWrappedToken, IList<SecurityToken> basicTokens, IList<SecurityToken> endorsingTokens,
           IList<SecurityToken> signedEndorsingTokens, IList<SecurityToken> signedTokens, Dictionary<SecurityToken, ReadOnlyCollection<IAuthorizationPolicy>> tokenPoliciesMapping)
        {
            ReadOnlyCollection<IAuthorizationPolicy> protectionTokenPolicies;
            if (isWrappedToken)
            {
                protectionTokenPolicies = EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance;
            }
            else
            {
                protectionTokenPolicies = tokenPoliciesMapping[protectionToken];
            }
            SecurityMessageProperty security = SecurityMessageProperty.GetOrCreate(message);
            security.ProtectionToken = new SecurityTokenSpecification(protectionToken, protectionTokenPolicies);
            AddSupportingTokenSpecification(security, basicTokens, endorsingTokens, signedEndorsingTokens, signedTokens, tokenPoliciesMapping);
            security.ServiceSecurityContext = new ServiceSecurityContext(security.GetInitiatorTokenAuthorizationPolicies());
        }
 
        // helper method for attaching the server claims in a symmetric security protocol
        protected void DoIdentityCheckAndAttachInitiatorSecurityProperty(Message message, SecurityToken protectionToken, ReadOnlyCollection<IAuthorizationPolicy> protectionTokenPolicies)
        {
            AuthorizationContext protectionAuthContext = EnsureIncomingIdentity(message, protectionToken, protectionTokenPolicies);
            SecurityMessageProperty security = SecurityMessageProperty.GetOrCreate(message);
            security.ProtectionToken = new SecurityTokenSpecification(protectionToken, protectionTokenPolicies);
            security.ServiceSecurityContext = new ServiceSecurityContext(protectionAuthContext, protectionTokenPolicies ?? EmptyReadOnlyCollection<IAuthorizationPolicy>.Instance);
        }
 
        protected AuthorizationContext EnsureIncomingIdentity(Message message, SecurityToken token, ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)
        {
            if (token == null)
            {
                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.NoSigningTokenAvailableToDoIncomingIdentityCheck)), message);
            }
            AuthorizationContext authContext = (authorizationPolicies != null) ? AuthorizationContext.CreateDefaultAuthorizationContext(authorizationPolicies) : null;
            if (this.factory.IdentityVerifier != null)
            {
                if (this.Target == null)
                {
                    throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.NoOutgoingEndpointAddressAvailableForDoingIdentityCheckOnReply)), message);
                }
 
                this.factory.IdentityVerifier.EnsureIncomingIdentity(this.Target, authContext);
            }
            return authContext;
        }
 
        protected void EnsureOutgoingIdentity(SecurityToken token, SecurityTokenAuthenticator authenticator)
        {
            if (object.ReferenceEquals(token, this.identityVerifiedToken))
            {
                return;
            }
            if (this.factory.IdentityVerifier == null)
            {
                return;
            }
            if (this.Target == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.NoOutgoingEndpointAddressAvailableForDoingIdentityCheck)));
            }
            ReadOnlyCollection<IAuthorizationPolicy> authzPolicies = authenticator.ValidateToken(token);
            this.factory.IdentityVerifier.EnsureOutgoingIdentity(this.Target, authzPolicies);
            if (this.CacheIdentityCheckResultForToken)
            {
                this.identityVerifiedToken = token;
            }
        }
 
        protected SecurityProtocolCorrelationState GetCorrelationState(SecurityToken correlationToken)
        {
            return new SecurityProtocolCorrelationState(correlationToken);
        }
 
        protected SecurityProtocolCorrelationState GetCorrelationState(SecurityToken correlationToken, ReceiveSecurityHeader securityHeader)
        {
            SecurityProtocolCorrelationState result = new SecurityProtocolCorrelationState(correlationToken);
            if (securityHeader.MaintainSignatureConfirmationState && !this.factory.ActAsInitiator)
            {
                result.SignatureConfirmations = securityHeader.GetSentSignatureValues();
            }
            return result;
        }
 
        protected SecurityToken GetCorrelationToken(SecurityProtocolCorrelationState[] correlationStates)
        {
            SecurityToken token = null;
            if (correlationStates != null)
            {
                for (int i = 0; i < correlationStates.Length; ++i)
                {
                    if (correlationStates[i].Token == null)
                        continue;
                    if (token == null)
                    {
                        token = correlationStates[i].Token;
                    }
                    else if (!object.ReferenceEquals(token, correlationStates[i].Token))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.MultipleCorrelationTokensFound)));
                    }
                }
            }
            if (token == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.NoCorrelationTokenFound)));
            }
            return token;
        }
 
 
        protected SecurityToken GetCorrelationToken(SecurityProtocolCorrelationState correlationState)
        {
            if (correlationState == null || correlationState.Token == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.CannotFindCorrelationStateForApplyingSecurity)));
            }
            return correlationState.Token;
        }
 
        protected static void EnsureNonWrappedToken(SecurityToken token, Message message)
        {
            if (token is WrappedKeySecurityToken)
            {
                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenNotExpectedInSecurityHeader, token)), message);
            }
        }
 
        protected SecurityToken GetTokenAndEnsureOutgoingIdentity(SecurityTokenProvider provider, bool isEncryptionOn, TimeSpan timeout, SecurityTokenAuthenticator authenticator)
        {
            SecurityToken token = GetToken(provider, this.Target, timeout);
            if (isEncryptionOn)
            {
                EnsureOutgoingIdentity(token, authenticator);
            }
            return token;
        }
 
        protected SendSecurityHeader ConfigureSendSecurityHeader(Message message, string actor, IList<SupportingTokenSpecification> supportingTokens, SecurityProtocolCorrelationState correlationState)
        {
            MessageSecurityProtocolFactory factory = this.MessageSecurityProtocolFactory;
            SendSecurityHeader securityHeader = CreateSendSecurityHeader(message, actor, factory);
            securityHeader.SignThenEncrypt = factory.MessageProtectionOrder != MessageProtectionOrder.EncryptBeforeSign;
            // If ProtectTokens is enabled then we make sure that both the client side and the service side sign the primary token 
            // ( if it is an issued token, the check exists in sendsecurityheader)in the primary signature while sending a message.
            securityHeader.ShouldProtectTokens = factory.SecurityBindingElement.ProtectTokens;
            securityHeader.EncryptPrimarySignature = factory.MessageProtectionOrder == MessageProtectionOrder.SignBeforeEncryptAndEncryptSignature;
 
            if (factory.DoRequestSignatureConfirmation && correlationState != null)
            {
                if (factory.ActAsInitiator)
                {
                    securityHeader.MaintainSignatureConfirmationState = true;
                    securityHeader.CorrelationState = correlationState;
                }
                else if (correlationState.SignatureConfirmations != null)
                {
                    securityHeader.AddSignatureConfirmations(correlationState.SignatureConfirmations);
                }
            }
 
            string action = message.Headers.Action;
            if (this.factory.ApplyIntegrity)
            {
                securityHeader.SignatureParts = this.factory.GetOutgoingSignatureParts(action);
            }
 
            if (factory.ApplyConfidentiality)
            {
                securityHeader.EncryptionParts = this.factory.GetOutgoingEncryptionParts(action);
            }
            AddSupportingTokens(securityHeader, supportingTokens);
            return securityHeader;
        }
 
        protected ReceiveSecurityHeader CreateSecurityHeader(Message message, string actor, MessageDirection transferDirection, SecurityStandardsManager standardsManager)
        {
            standardsManager = standardsManager ?? this.factory.StandardsManager;
            ReceiveSecurityHeader securityHeader = standardsManager.CreateReceiveSecurityHeader(message, actor,
               this.factory.IncomingAlgorithmSuite, transferDirection);
            securityHeader.Layout = this.factory.SecurityHeaderLayout;
            securityHeader.MaxReceivedMessageSize = factory.SecurityBindingElement.MaxReceivedMessageSize;
            securityHeader.ReaderQuotas = factory.SecurityBindingElement.ReaderQuotas;
            if (this.factory.ExpectKeyDerivation)
            {
                securityHeader.DerivedTokenAuthenticator = this.factory.DerivedKeyTokenAuthenticator;
            }
            return securityHeader;
        }
 
        bool HasCorrelationState(SecurityProtocolCorrelationState[] correlationState)
        {
            if (correlationState == null || correlationState.Length == 0)
            {
                return false;
            }
            else if (correlationState.Length == 1 && correlationState[0] == null)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
 
        protected ReceiveSecurityHeader ConfigureReceiveSecurityHeader(Message message, string actor, SecurityProtocolCorrelationState[] correlationStates, out IList<SupportingTokenAuthenticatorSpecification> supportingAuthenticators)
        {
            return ConfigureReceiveSecurityHeader(message, actor, correlationStates, null, out supportingAuthenticators);
        }
 
        protected ReceiveSecurityHeader ConfigureReceiveSecurityHeader(Message message, string actor, SecurityProtocolCorrelationState[] correlationStates, SecurityStandardsManager standardsManager, out IList<SupportingTokenAuthenticatorSpecification> supportingAuthenticators)
        {
            MessageSecurityProtocolFactory factory = this.MessageSecurityProtocolFactory;
            MessageDirection direction = factory.ActAsInitiator ? MessageDirection.Output : MessageDirection.Input;
            ReceiveSecurityHeader securityHeader = CreateSecurityHeader(message, actor, direction, standardsManager);
 
            string action = message.Headers.Action;
            supportingAuthenticators = GetSupportingTokenAuthenticatorsAndSetExpectationFlags(this.factory, message, securityHeader);
            if (factory.RequireIntegrity || securityHeader.ExpectSignedTokens)
            {
                securityHeader.RequiredSignatureParts = factory.GetIncomingSignatureParts(action);
            }
            if (factory.RequireConfidentiality || securityHeader.ExpectBasicTokens)
            {
                securityHeader.RequiredEncryptionParts = factory.GetIncomingEncryptionParts(action);
            }
 
            securityHeader.ExpectEncryption = factory.RequireConfidentiality || securityHeader.ExpectBasicTokens;
            securityHeader.ExpectSignature = factory.RequireIntegrity || securityHeader.ExpectSignedTokens;
            securityHeader.SetRequiredProtectionOrder(factory.MessageProtectionOrder);
 
            // On the receiving side if protectTokens is enabled
            // 1. If we are service, we make sure that the client always signs the primary token( can be any token type)else we throw.
            //    But currently the service can sign the primary token in reply only if the primary token is an issued token 
            // 2. If we are client, we do not care if the service signs the primary token or not. Otherwise it will be impossible to have a wcf client /service talk to each other unless we 
            // either use a symmetric binding with issued tokens or asymmetric bindings with both the intiator and recipient parameters being issued tokens( later one is rare).
            securityHeader.RequireSignedPrimaryToken = !factory.ActAsInitiator && factory.SecurityBindingElement.ProtectTokens;
 
            if (factory.ActAsInitiator && factory.DoRequestSignatureConfirmation && HasCorrelationState(correlationStates))
            {
                securityHeader.MaintainSignatureConfirmationState = true;
                securityHeader.ExpectSignatureConfirmation = true;
            }
            else if (!factory.ActAsInitiator && factory.DoRequestSignatureConfirmation)
            {
                securityHeader.MaintainSignatureConfirmationState = true;
            }
            else
            {
                securityHeader.MaintainSignatureConfirmationState = false;
            }
            return securityHeader;
        }
 
        protected void ProcessSecurityHeader(ReceiveSecurityHeader securityHeader, ref Message message,
            SecurityToken requiredSigningToken, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
 
            securityHeader.ReplayDetectionEnabled = this.factory.DetectReplays;
            securityHeader.SetTimeParameters(this.factory.NonceCache, this.factory.ReplayWindow, this.factory.MaxClockSkew);
 
            securityHeader.Process(timeoutHelper.RemainingTime(), SecurityUtils.GetChannelBindingFromMessage(message), this.factory.ExtendedProtectionPolicy);
            if (this.factory.AddTimestamp && securityHeader.Timestamp == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperWarning(new MessageSecurityException(SR.GetString(SR.RequiredTimestampMissingInSecurityHeader)));
            }
 
            if (requiredSigningToken != null && requiredSigningToken != securityHeader.SignatureToken)
            {
                throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.ReplyWasNotSignedWithRequiredSigningToken)), message);
            }
 
            if (this.DoAutomaticEncryptionMatch)
            {
                SecurityUtils.EnsureExpectedSymmetricMatch(securityHeader.SignatureToken, securityHeader.EncryptionToken, message);
            }
 
            if (securityHeader.MaintainSignatureConfirmationState && this.factory.ActAsInitiator)
            {
                CheckSignatureConfirmation(securityHeader, correlationStates);
            }
 
            message = securityHeader.ProcessedMessage;
        }
 
        protected void CheckSignatureConfirmation(ReceiveSecurityHeader securityHeader, SecurityProtocolCorrelationState[] correlationStates)
        {
            SignatureConfirmations receivedConfirmations = securityHeader.GetSentSignatureConfirmations();
            SignatureConfirmations sentSignatures = null;
            if (correlationStates != null)
            {
                for (int i = 0; i < correlationStates.Length; ++i)
                {
                    if (correlationStates[i].SignatureConfirmations != null)
                    {
                        sentSignatures = correlationStates[i].SignatureConfirmations;
                        break;
                    }
                }
            }
            if (sentSignatures == null)
            {
                if (receivedConfirmations != null && receivedConfirmations.Count > 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.FoundUnexpectedSignatureConfirmations)));
                }
                return;
            }
            bool allSignaturesConfirmed = false;
            if (receivedConfirmations != null && sentSignatures.Count == receivedConfirmations.Count)
            {
                bool[] matchingSigIndexes = new bool[sentSignatures.Count];
                for (int i = 0; i < sentSignatures.Count; ++i)
                {
                    byte[] sentSignature;
                    bool wasSentSigEncrypted;
                    sentSignatures.GetConfirmation(i, out sentSignature, out wasSentSigEncrypted);
                    for (int j = 0; j < receivedConfirmations.Count; ++j)
                    {
                        byte[] receivedSignature;
                        bool wasReceivedSigEncrypted;
                        if (matchingSigIndexes[j])
                        {
                            continue;
                        }
                        receivedConfirmations.GetConfirmation(j, out receivedSignature, out wasReceivedSigEncrypted);
                        if ((wasReceivedSigEncrypted == wasSentSigEncrypted) && CryptoHelper.IsEqual(receivedSignature, sentSignature))
                        {
                            matchingSigIndexes[j] = true;
                            break;
                        }
                    }
                }
                int k;
                for (k = 0; k < matchingSigIndexes.Length; ++k)
                {
                    if (!matchingSigIndexes[k])
                    {
                        break;
                    }
                }
                if (k == matchingSigIndexes.Length)
                {
                    allSignaturesConfirmed = true;
                }
            }
            if (!allSignaturesConfirmed)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.NotAllSignaturesConfirmed)));
            }
        }
 
        public override void SecureOutgoingMessage(ref Message message, TimeSpan timeout)
        {
            try
            {
                this.CommunicationObject.ThrowIfClosedOrNotOpen();
                ValidateOutgoingState(message);
                if (!this.RequiresOutgoingSecurityProcessing && message.Properties.Security == null)
                {
                    return;
                }
 
                SecureOutgoingMessageCore(ref message, timeout, null);
                base.OnOutgoingMessageSecured(message);
            }
            catch (Exception exception)
            {
                // Always immediately rethrow fatal exceptions.
                if (Fx.IsFatal(exception)) throw;
 
                base.OnSecureOutgoingMessageFailure(message);
                throw;
            }
        }
 
        public override SecurityProtocolCorrelationState SecureOutgoingMessage(ref Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState)
        {
            try
            {
                this.CommunicationObject.ThrowIfClosedOrNotOpen();
                ValidateOutgoingState(message);
                if (!this.RequiresOutgoingSecurityProcessing && message.Properties.Security == null)
                {
                    return null;
                }
                SecurityProtocolCorrelationState newCorrelationState = SecureOutgoingMessageCore(ref message, timeout, correlationState);
                base.OnOutgoingMessageSecured(message);
                return newCorrelationState;
            }
            catch (Exception exception)
            {
                // Always immediately rethrow fatal exceptions.
                if (Fx.IsFatal(exception)) throw;
 
                base.OnSecureOutgoingMessageFailure(message);
                throw;
            }
        }
 
        protected abstract SecurityProtocolCorrelationState SecureOutgoingMessageCore(ref Message message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState);
 
        void ValidateOutgoingState(Message message)
        {
            if (this.PerformIncomingAndOutgoingMessageExpectationChecks && !this.factory.ExpectOutgoingMessages)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityBindingNotSetUpToProcessOutgoingMessages)));
            }
            if (message == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
            }
        }
 
        public override void VerifyIncomingMessage(ref Message message, TimeSpan timeout)
        {
            try
            {
                this.CommunicationObject.ThrowIfClosedOrNotOpen();
                if (this.PerformIncomingAndOutgoingMessageExpectationChecks && !factory.ExpectIncomingMessages)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityBindingNotSetUpToProcessIncomingMessages)));
                }
                if (message == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
                }
                if (!this.RequiresIncomingSecurityProcessing(message))
                {
                    return;
                }
                string actor = string.Empty; // message.Version.Envelope.UltimateDestinationActor;
                VerifyIncomingMessageCore(ref message, actor, timeout, null);
                base.OnIncomingMessageVerified(message);
            }
            catch (MessageSecurityException e)
            {
                base.OnVerifyIncomingMessageFailure(message, e);
                throw;
            }
            catch (Exception e)
            {
                // Always immediately rethrow fatal exceptions.
                if (Fx.IsFatal(e)) throw;
 
                base.OnVerifyIncomingMessageFailure(message, e);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.MessageSecurityVerificationFailed), e));
            }
        }
 
        public override SecurityProtocolCorrelationState VerifyIncomingMessage(ref Message message, TimeSpan timeout, params SecurityProtocolCorrelationState[] correlationStates)
        {
            try
            {
                this.CommunicationObject.ThrowIfClosedOrNotOpen();
                if (this.PerformIncomingAndOutgoingMessageExpectationChecks && !factory.ExpectIncomingMessages)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SecurityBindingNotSetUpToProcessIncomingMessages)));
                }
                if (message == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("message");
                }
                if (!this.RequiresIncomingSecurityProcessing(message))
                {
                    return null;
                }
                string actor = string.Empty; // message.Version.Envelope.UltimateDestinationActor;
                SecurityProtocolCorrelationState newCorrelationState = VerifyIncomingMessageCore(ref message, actor, timeout, correlationStates);
                base.OnIncomingMessageVerified(message);
                return newCorrelationState;
            }
            catch (MessageSecurityException e)
            {
                base.OnVerifyIncomingMessageFailure(message, e);
                throw;
            }
            catch (Exception e)
            {
                // Always immediately rethrow fatal exceptions.
                if (Fx.IsFatal(e)) throw;
 
                base.OnVerifyIncomingMessageFailure(message, e);
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.MessageSecurityVerificationFailed), e));
            }
        }
 
        protected abstract SecurityProtocolCorrelationState VerifyIncomingMessageCore(ref Message message, string actor, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates);
 
        internal SecurityProtocolCorrelationState GetSignatureConfirmationCorrelationState(SecurityProtocolCorrelationState oldCorrelationState, SecurityProtocolCorrelationState newCorrelationState)
        {
            if (this.factory.ActAsInitiator)
            {
                return newCorrelationState;
            }
            else
            {
                return oldCorrelationState;
            }
        }
 
        protected abstract class GetOneTokenAndSetUpSecurityAsyncResult : GetSupportingTokensAsyncResult
        {
            readonly MessageSecurityProtocol binding;
            readonly SecurityTokenProvider provider;
            Message message;
            readonly bool doIdentityChecks;
            SecurityTokenAuthenticator identityCheckAuthenticator;
            static AsyncCallback getTokenCompleteCallback = Fx.ThunkCallback(new AsyncCallback(GetTokenCompleteCallback));
            SecurityProtocolCorrelationState newCorrelationState;
            SecurityProtocolCorrelationState oldCorrelationState;
            TimeoutHelper timeoutHelper;
 
            public GetOneTokenAndSetUpSecurityAsyncResult(Message m, MessageSecurityProtocol binding, SecurityTokenProvider provider,
                bool doIdentityChecks, SecurityTokenAuthenticator identityCheckAuthenticator, SecurityProtocolCorrelationState oldCorrelationState, TimeSpan timeout, AsyncCallback callback, object state)
                : base(m, binding, timeout, callback, state)
            {
                this.message = m;
                this.binding = binding;
                this.provider = provider;
                this.doIdentityChecks = doIdentityChecks;
                this.oldCorrelationState = oldCorrelationState;
                this.identityCheckAuthenticator = identityCheckAuthenticator;
            }
 
            protected MessageSecurityProtocol Binding
            {
                get { return this.binding; }
            }
 
            protected SecurityProtocolCorrelationState NewCorrelationState
            {
                get { return this.newCorrelationState; }
            }
 
            protected SecurityProtocolCorrelationState OldCorrelationState
            {
                get { return this.oldCorrelationState; }
            }
 
            internal static Message End(IAsyncResult result, out SecurityProtocolCorrelationState newCorrelationState)
            {
                GetOneTokenAndSetUpSecurityAsyncResult self = AsyncResult.End<GetOneTokenAndSetUpSecurityAsyncResult>(result);
                newCorrelationState = self.newCorrelationState;
                return self.message;
            }
 
            bool OnGetTokenComplete(SecurityToken token)
            {
                if (token == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenProviderCannotGetTokensForTarget, this.binding.Target)));
                }
                if (this.doIdentityChecks)
                {
                    this.binding.EnsureOutgoingIdentity(token, this.identityCheckAuthenticator);
                }
                OnGetTokenDone(ref this.message, token, timeoutHelper.RemainingTime());
                return true;
            }
 
            protected abstract void OnGetTokenDone(ref Message message, SecurityToken token, TimeSpan timeout);
 
            static void GetTokenCompleteCallback(IAsyncResult result)
            {
                if (result == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
                }
                if (result.CompletedSynchronously)
                {
                    return;
                }
                GetOneTokenAndSetUpSecurityAsyncResult self = result.AsyncState as GetOneTokenAndSetUpSecurityAsyncResult;
                if (self == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SR.GetString(SR.InvalidAsyncResult));
                }
                Exception completionException = null;
                bool completeSelf = false;
                try
                {
                    SecurityToken token = self.provider.EndGetToken(result);
                    completeSelf = self.OnGetTokenComplete(token);
                }
#pragma warning suppress 56500 // covered by FxCOP
                catch (Exception e)
                {
                    // Always immediately rethrow fatal exceptions.
                    if (Fx.IsFatal(e)) throw;
 
                    completeSelf = true;
                    completionException = e;
                }
                if (completeSelf)
                {
                    self.Complete(false, completionException);
                }
            }
 
            protected void SetCorrelationToken(SecurityToken token)
            {
                newCorrelationState = new SecurityProtocolCorrelationState(token);
            }
 
            protected override bool OnGetSupportingTokensDone(TimeSpan timeout)
            {
                this.timeoutHelper = new TimeoutHelper(timeout);
                IAsyncResult result = this.provider.BeginGetToken(timeoutHelper.RemainingTime(), getTokenCompleteCallback, this);
                if (!result.CompletedSynchronously)
                {
                    return false;
 
                }
                SecurityToken token = this.provider.EndGetToken(result);
                return this.OnGetTokenComplete(token);
            }
        }
 
        // note: identity check done only on token obtained from first
        // token provider; either or both token providers may be null;
        // get token calls are skipped for null providers.
        protected abstract class GetTwoTokensAndSetUpSecurityAsyncResult : GetSupportingTokensAsyncResult
        {
            readonly MessageSecurityProtocol binding;
            readonly SecurityTokenProvider primaryProvider;
            readonly SecurityTokenProvider secondaryProvider;
            Message message;
            readonly bool doIdentityChecks;
            SecurityTokenAuthenticator identityCheckAuthenticator;
            SecurityToken primaryToken;
            static readonly AsyncCallback getPrimaryTokenCompleteCallback = Fx.ThunkCallback(new AsyncCallback(GetPrimaryTokenCompleteCallback));
            static readonly AsyncCallback getSecondaryTokenCompleteCallback = Fx.ThunkCallback(new AsyncCallback(GetSecondaryTokenCompleteCallback));
            SecurityProtocolCorrelationState newCorrelationState;
            SecurityProtocolCorrelationState oldCorrelationState;
            TimeoutHelper timeoutHelper;
 
            public GetTwoTokensAndSetUpSecurityAsyncResult(Message m, MessageSecurityProtocol binding,
                SecurityTokenProvider primaryProvider, SecurityTokenProvider secondaryProvider, bool doIdentityChecks, SecurityTokenAuthenticator identityCheckAuthenticator,
                SecurityProtocolCorrelationState oldCorrelationState,
                TimeSpan timeout,
                AsyncCallback callback, object state)
                : base(m, binding, timeout, callback, state)
            {
                this.message = m;
                this.binding = binding;
                this.primaryProvider = primaryProvider;
                this.secondaryProvider = secondaryProvider;
                this.doIdentityChecks = doIdentityChecks;
                this.identityCheckAuthenticator = identityCheckAuthenticator;
                this.oldCorrelationState = oldCorrelationState;
            }
 
            protected MessageSecurityProtocol Binding
            {
                get { return this.binding; }
            }
 
            protected SecurityProtocolCorrelationState NewCorrelationState
            {
                get { return this.newCorrelationState; }
            }
 
            protected SecurityProtocolCorrelationState OldCorrelationState
            {
                get { return this.oldCorrelationState; }
            }
 
            internal static Message End(IAsyncResult result, out SecurityProtocolCorrelationState newCorrelationState)
            {
                GetTwoTokensAndSetUpSecurityAsyncResult self = AsyncResult.End<GetTwoTokensAndSetUpSecurityAsyncResult>(result);
                newCorrelationState = self.newCorrelationState;
                return self.message;
            }
 
            bool OnGetPrimaryTokenComplete(SecurityToken token)
            {
                return OnGetPrimaryTokenComplete(token, false);
            }
 
            bool OnGetPrimaryTokenComplete(SecurityToken token, bool primaryCallSkipped)
            {
                if (!primaryCallSkipped)
                {
                    if (token == null)
                    {
                        throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenProviderCannotGetTokensForTarget, this.binding.Target)), this.message);
                    }
                    if (this.doIdentityChecks)
                    {
                        this.binding.EnsureOutgoingIdentity(token, this.identityCheckAuthenticator);
                    }
                }
                this.primaryToken = token;
 
                if (this.secondaryProvider == null)
                {
                    return this.OnGetSecondaryTokenComplete(null, true);
                }
                else
                {
                    IAsyncResult result = this.secondaryProvider.BeginGetToken(this.timeoutHelper.RemainingTime(), getSecondaryTokenCompleteCallback, this);
                    if (!result.CompletedSynchronously)
                    {
                        return false;
                    }
                    SecurityToken token2 = this.secondaryProvider.EndGetToken(result);
                    return this.OnGetSecondaryTokenComplete(token2);
                }
            }
 
            bool OnGetSecondaryTokenComplete(SecurityToken token)
            {
                return OnGetSecondaryTokenComplete(token, false);
            }
 
            bool OnGetSecondaryTokenComplete(SecurityToken token, bool secondaryCallSkipped)
            {
                if (!secondaryCallSkipped && token == null)
                {
                    throw TraceUtility.ThrowHelperError(new MessageSecurityException(SR.GetString(SR.TokenProviderCannotGetTokensForTarget, this.binding.Target)), this.message);
                }
                OnBothGetTokenCallsDone(ref this.message, this.primaryToken, token, timeoutHelper.RemainingTime());
                return true;
            }
 
            protected abstract void OnBothGetTokenCallsDone(ref Message message, SecurityToken primaryToken, SecurityToken secondaryToken, TimeSpan timeout);
 
            static void GetPrimaryTokenCompleteCallback(IAsyncResult result)
            {
                if (result == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
                }
                if (result.CompletedSynchronously)
                {
                    return;
                }
                GetTwoTokensAndSetUpSecurityAsyncResult self = result.AsyncState as GetTwoTokensAndSetUpSecurityAsyncResult;
                if (self == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SR.GetString(SR.InvalidAsyncResult));
                }
                bool completeSelf = false;
                Exception completionException = null;
                try
                {
                    SecurityToken token = self.primaryProvider.EndGetToken(result);
                    completeSelf = self.OnGetPrimaryTokenComplete(token);
                }
#pragma warning suppress 56500 // covered by FxCOP
                catch (Exception e)
                {
                    // Always immediately rethrow fatal exceptions.
                    if (Fx.IsFatal(e)) throw;
 
                    completeSelf = true;
                    completionException = e;
                }
                if (completeSelf)
                {
                    self.Complete(false, completionException);
                }
            }
 
            static void GetSecondaryTokenCompleteCallback(IAsyncResult result)
            {
                if (result == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("result");
                }
                if (result.CompletedSynchronously)
                {
                    return;
                }
                GetTwoTokensAndSetUpSecurityAsyncResult self = result.AsyncState as GetTwoTokensAndSetUpSecurityAsyncResult;
                if (self == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SR.GetString(SR.InvalidAsyncResult));
                }
                bool completeSelf = false;
                Exception completionException = null;
                try
                {
                    SecurityToken token = self.secondaryProvider.EndGetToken(result);
                    completeSelf = self.OnGetSecondaryTokenComplete(token);
                }
#pragma warning suppress 56500 // covered by FxCOP
                catch (Exception e)
                {
                    // Always immediately rethrow fatal exceptions.
                    if (Fx.IsFatal(e)) throw;
 
                    completeSelf = true;
                    completionException = e;
                }
                if (completeSelf)
                {
                    self.Complete(false, completionException);
                }
            }
 
            protected void SetCorrelationToken(SecurityToken token)
            {
                newCorrelationState = new SecurityProtocolCorrelationState(token);
            }
 
            protected override bool OnGetSupportingTokensDone(TimeSpan timeout)
            {
                this.timeoutHelper = new TimeoutHelper(timeout);
                bool completeSelf = false;
                if (this.primaryProvider == null)
                {
                    completeSelf = this.OnGetPrimaryTokenComplete(null);
                }
                else
                {
                    IAsyncResult result = this.primaryProvider.BeginGetToken(this.timeoutHelper.RemainingTime(), getPrimaryTokenCompleteCallback, this);
                    if (result.CompletedSynchronously)
                    {
                        SecurityToken token = this.primaryProvider.EndGetToken(result);
                        completeSelf = this.OnGetPrimaryTokenComplete(token);
                    }
                }
                return completeSelf;
            }
        }
    }
}