File: FrameworkFork\System.ServiceModel\System\ServiceModel\Security\SecurityProtocolFactory.cs
Web Access
Project: src\src\dotnet-svcutil\lib\src\dotnet-svcutil-lib.csproj (dotnet-svcutil-lib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Runtime;
using System.Security.Authentication.ExtendedProtection;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Security.Tokens;
using System.Globalization;
 
namespace System.ServiceModel.Security
{
    /*
     * See
     * http://xws/gxa/main/specs/security/security_profiles/SecurityProfiles.doc
     * for details on security protocols
 
     * Concrete implementations are required to me thread safe after
     * Open() is called;
 
     * instances of concrete protocol factories are scoped to a
     * channel/listener factory;
 
     * Each channel/listener factory must have a
     * SecurityProtocolFactory set on it before open/first use; the
     * factory instance cannot be changed once the factory is opened
     * or listening;
 
     * security protocol instances are scoped to a channel and will be
     * created by the Create calls on protocol factories;
 
     * security protocol instances are required to be thread-safe.
 
     * for typical subclasses, factory wide state and immutable
     * settings are expected to be on the ProtocolFactory itself while
     * channel-wide state is maintained internally in each security
     * protocol instance;
 
     * the security protocol instance set on a channel cannot be
     * changed; however, the protocol instance may change internal
     * state; this covers RM's SCT renego case; by keeping state
     * change internal to protocol instances, we get better
     * coordination with concurrent message security on channels;
 
     * the primary pivot in creating a security protocol instance is
     * initiator (client) vs. responder (server), NOT sender vs
     * receiver
 
     * Create calls for input and reply channels will contain the
     * listener-wide state (if any) created by the corresponding call
     * on the factory;
 
     */
 
    // Whether we need to add support for targetting different SOAP roles is tracked by 19144
 
    internal abstract class SecurityProtocolFactory : ISecurityCommunicationObject
    {
        internal const bool defaultAddTimestamp = true;
        internal const bool defaultDeriveKeys = true;
        internal const bool defaultDetectReplays = true;
        internal const string defaultMaxClockSkewString = "00:05:00";
        internal const string defaultReplayWindowString = "00:05:00";
        internal static readonly TimeSpan defaultMaxClockSkew = TimeSpan.Parse(defaultMaxClockSkewString, CultureInfo.InvariantCulture);
        internal static readonly TimeSpan defaultReplayWindow = TimeSpan.Parse(defaultReplayWindowString, CultureInfo.InvariantCulture);
        internal const int defaultMaxCachedNonces = 900000;
        internal const string defaultTimestampValidityDurationString = "00:05:00";
        internal static readonly TimeSpan defaultTimestampValidityDuration = TimeSpan.Parse(defaultTimestampValidityDurationString, CultureInfo.InvariantCulture);
        internal const SecurityHeaderLayout defaultSecurityHeaderLayout = SecurityHeaderLayout.Strict;
 
        private static ReadOnlyCollection<SupportingTokenAuthenticatorSpecification> s_emptyTokenAuthenticators;
 
        private bool _actAsInitiator;
        private bool _isDuplexReply;
        private bool _addTimestamp = defaultAddTimestamp;
        private bool _detectReplays = defaultDetectReplays;
        private bool _expectIncomingMessages;
        private bool _expectOutgoingMessages;
        private SecurityAlgorithmSuite _incomingAlgorithmSuite = SecurityAlgorithmSuite.Default;
 
 
        // per receiver protocol factory lists
        private ICollection<SupportingTokenAuthenticatorSpecification> _channelSupportingTokenAuthenticatorSpecification;
        private Dictionary<string, ICollection<SupportingTokenAuthenticatorSpecification>> _scopedSupportingTokenAuthenticatorSpecification;
        private Dictionary<string, MergedSupportingTokenAuthenticatorSpecification> _mergedSupportingTokenAuthenticatorsMap;
 
        private int _maxCachedNonces = defaultMaxCachedNonces;
        private TimeSpan _maxClockSkew = defaultMaxClockSkew;
        private NonceCache _nonceCache = null;
        private SecurityAlgorithmSuite _outgoingAlgorithmSuite = SecurityAlgorithmSuite.Default;
        private TimeSpan _replayWindow = defaultReplayWindow;
        private SecurityStandardsManager _standardsManager = SecurityStandardsManager.DefaultInstance;
        private SecurityTokenManager _securityTokenManager;
        private SecurityBindingElement _securityBindingElement;
        private string _requestReplyErrorPropertyName;
        private NonValidatingSecurityTokenAuthenticator<DerivedKeySecurityToken> _derivedKeyTokenAuthenticator;
        private TimeSpan _timestampValidityDuration = defaultTimestampValidityDuration;
        private AuditLogLocation _auditLogLocation;
        private bool _suppressAuditFailure;
        private SecurityHeaderLayout _securityHeaderLayout;
        private AuditLevel _serviceAuthorizationAuditLevel;
        private AuditLevel _messageAuthenticationAuditLevel;
        private bool _expectKeyDerivation;
        private bool _expectChannelBasicTokens;
        private bool _expectChannelSignedTokens;
        private bool _expectChannelEndorsingTokens;
        private bool _expectSupportingTokens;
        private Uri _listenUri;
        private MessageSecurityVersion _messageSecurityVersion;
        private WrapperSecurityCommunicationObject _communicationObject;
        private Uri _privacyNoticeUri;
        private int _privacyNoticeVersion;
        private ExtendedProtectionPolicy _extendedProtectionPolicy;
        private BufferManager _streamBufferManager = null;
 
        protected SecurityProtocolFactory()
        {
            _channelSupportingTokenAuthenticatorSpecification = new Collection<SupportingTokenAuthenticatorSpecification>();
            _scopedSupportingTokenAuthenticatorSpecification = new Dictionary<string, ICollection<SupportingTokenAuthenticatorSpecification>>();
            _communicationObject = new WrapperSecurityCommunicationObject(this);
        }
 
        internal SecurityProtocolFactory(SecurityProtocolFactory factory)
            : this()
        {
            if (factory == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("factory");
            }
 
            _actAsInitiator = factory._actAsInitiator;
            _addTimestamp = factory._addTimestamp;
            _detectReplays = factory._detectReplays;
            _incomingAlgorithmSuite = factory._incomingAlgorithmSuite;
            _maxCachedNonces = factory._maxCachedNonces;
            _maxClockSkew = factory._maxClockSkew;
            _outgoingAlgorithmSuite = factory._outgoingAlgorithmSuite;
            _replayWindow = factory._replayWindow;
            _channelSupportingTokenAuthenticatorSpecification = new Collection<SupportingTokenAuthenticatorSpecification>(new List<SupportingTokenAuthenticatorSpecification>(factory._channelSupportingTokenAuthenticatorSpecification));
            _scopedSupportingTokenAuthenticatorSpecification = new Dictionary<string, ICollection<SupportingTokenAuthenticatorSpecification>>(factory._scopedSupportingTokenAuthenticatorSpecification);
            _standardsManager = factory._standardsManager;
            _timestampValidityDuration = factory._timestampValidityDuration;
            _auditLogLocation = factory._auditLogLocation;
            _suppressAuditFailure = factory._suppressAuditFailure;
            _serviceAuthorizationAuditLevel = factory._serviceAuthorizationAuditLevel;
            _messageAuthenticationAuditLevel = factory._messageAuthenticationAuditLevel;
            if (factory._securityBindingElement != null)
            {
                _securityBindingElement = (SecurityBindingElement)factory._securityBindingElement.Clone();
            }
            _securityTokenManager = factory._securityTokenManager;
            _privacyNoticeUri = factory._privacyNoticeUri;
            _privacyNoticeVersion = factory._privacyNoticeVersion;
            _extendedProtectionPolicy = factory._extendedProtectionPolicy;
            _nonceCache = factory._nonceCache;
        }
 
        protected WrapperSecurityCommunicationObject CommunicationObject
        {
            get { return _communicationObject; }
        }
 
        // The ActAsInitiator value is set automatically on Open and
        // remains unchanged thereafter.  ActAsInitiator is true for
        // the initiator of the message exchange, such as the sender
        // of a datagram, sender of a request and sender of either leg
        // of a duplex exchange.
        public bool ActAsInitiator
        {
            get
            {
                return _actAsInitiator;
            }
        }
 
        public BufferManager StreamBufferManager
        {
            get
            {
                if (_streamBufferManager == null)
                {
                    _streamBufferManager = BufferManager.CreateBufferManager(0, int.MaxValue);
                }
 
                return _streamBufferManager;
            }
            set
            {
                _streamBufferManager = value;
            }
        }
 
        internal bool IsDuplexReply
        {
            get
            {
                return _isDuplexReply;
            }
            set
            {
                _isDuplexReply = value;
            }
        }
 
        public bool AddTimestamp
        {
            get
            {
                return _addTimestamp;
            }
            set
            {
                ThrowIfImmutable();
                _addTimestamp = value;
            }
        }
 
        public AuditLogLocation AuditLogLocation
        {
            get
            {
                return _auditLogLocation;
            }
            set
            {
                ThrowIfImmutable();
                AuditLogLocationHelper.Validate(value);
                _auditLogLocation = value;
            }
        }
 
        public bool SuppressAuditFailure
        {
            get
            {
                return _suppressAuditFailure;
            }
            set
            {
                ThrowIfImmutable();
                _suppressAuditFailure = value;
            }
        }
 
        public AuditLevel ServiceAuthorizationAuditLevel
        {
            get
            {
                return _serviceAuthorizationAuditLevel;
            }
            set
            {
                ThrowIfImmutable();
                AuditLevelHelper.Validate(value);
                _serviceAuthorizationAuditLevel = value;
            }
        }
 
        public AuditLevel MessageAuthenticationAuditLevel
        {
            get
            {
                return _messageAuthenticationAuditLevel;
            }
            set
            {
                ThrowIfImmutable();
                AuditLevelHelper.Validate(value);
                _messageAuthenticationAuditLevel = value;
            }
        }
 
 
        public bool DetectReplays
        {
            get
            {
                return _detectReplays;
            }
            set
            {
                ThrowIfImmutable();
                _detectReplays = value;
            }
        }
 
        public Uri PrivacyNoticeUri
        {
            get
            {
                return _privacyNoticeUri;
            }
            set
            {
                ThrowIfImmutable();
                _privacyNoticeUri = value;
            }
        }
 
        public int PrivacyNoticeVersion
        {
            get
            {
                return _privacyNoticeVersion;
            }
            set
            {
                ThrowIfImmutable();
                _privacyNoticeVersion = value;
            }
        }
 
        private static ReadOnlyCollection<SupportingTokenAuthenticatorSpecification> EmptyTokenAuthenticators
        {
            get
            {
                if (s_emptyTokenAuthenticators == null)
                {
                    s_emptyTokenAuthenticators = new ReadOnlyCollection<SupportingTokenAuthenticatorSpecification>(new SupportingTokenAuthenticatorSpecification[0]);
                }
                return s_emptyTokenAuthenticators;
            }
        }
 
        internal NonValidatingSecurityTokenAuthenticator<DerivedKeySecurityToken> DerivedKeyTokenAuthenticator
        {
            get
            {
                return _derivedKeyTokenAuthenticator;
            }
        }
 
        internal bool ExpectIncomingMessages
        {
            get
            {
                return _expectIncomingMessages;
            }
        }
 
        internal bool ExpectOutgoingMessages
        {
            get
            {
                return _expectOutgoingMessages;
            }
        }
 
        internal bool ExpectKeyDerivation
        {
            get { return _expectKeyDerivation; }
            set { _expectKeyDerivation = value; }
        }
 
        internal bool ExpectSupportingTokens
        {
            get { return _expectSupportingTokens; }
            set { _expectSupportingTokens = value; }
        }
 
        public SecurityAlgorithmSuite IncomingAlgorithmSuite
        {
            get
            {
                return _incomingAlgorithmSuite;
            }
            set
            {
                ThrowIfImmutable();
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
                }
                _incomingAlgorithmSuite = value;
            }
        }
 
        protected bool IsReadOnly
        {
            get
            {
                return this.CommunicationObject.State != CommunicationState.Created;
            }
        }
 
        public int MaxCachedNonces
        {
            get
            {
                return _maxCachedNonces;
            }
            set
            {
                ThrowIfImmutable();
                if (value <= 0)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
                }
                _maxCachedNonces = value;
            }
        }
 
        public TimeSpan MaxClockSkew
        {
            get
            {
                return _maxClockSkew;
            }
            set
            {
                ThrowIfImmutable();
                if (value < TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value"));
                }
                _maxClockSkew = value;
            }
        }
 
        public NonceCache NonceCache
        {
            get
            {
                return _nonceCache;
            }
            set
            {
                ThrowIfImmutable();
                _nonceCache = value;
            }
        }
 
        public SecurityAlgorithmSuite OutgoingAlgorithmSuite
        {
            get
            {
                return _outgoingAlgorithmSuite;
            }
            set
            {
                ThrowIfImmutable();
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
                }
                _outgoingAlgorithmSuite = value;
            }
        }
 
        public TimeSpan ReplayWindow
        {
            get
            {
                return _replayWindow;
            }
            set
            {
                ThrowIfImmutable();
                if (value <= TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SRServiceModel.TimeSpanMustbeGreaterThanTimeSpanZero));
                }
                _replayWindow = value;
            }
        }
 
        public ICollection<SupportingTokenAuthenticatorSpecification> ChannelSupportingTokenAuthenticatorSpecification
        {
            get
            {
                return _channelSupportingTokenAuthenticatorSpecification;
            }
        }
 
        public Dictionary<string, ICollection<SupportingTokenAuthenticatorSpecification>> ScopedSupportingTokenAuthenticatorSpecification
        {
            get
            {
                return _scopedSupportingTokenAuthenticatorSpecification;
            }
        }
 
        public SecurityBindingElement SecurityBindingElement
        {
            get { return _securityBindingElement; }
            set
            {
                ThrowIfImmutable();
                if (value != null)
                {
                    value = (SecurityBindingElement)value.Clone();
                }
                _securityBindingElement = value;
            }
        }
 
        public SecurityTokenManager SecurityTokenManager
        {
            get { return _securityTokenManager; }
            set
            {
                ThrowIfImmutable();
                _securityTokenManager = value;
            }
        }
 
        public virtual bool SupportsDuplex
        {
            get
            {
                return false;
            }
        }
 
        public SecurityHeaderLayout SecurityHeaderLayout
        {
            get
            {
                return _securityHeaderLayout;
            }
            set
            {
                ThrowIfImmutable();
                _securityHeaderLayout = value;
            }
        }
 
        public virtual bool SupportsReplayDetection
        {
            get
            {
                return true;
            }
        }
 
        public virtual bool SupportsRequestReply
        {
            get
            {
                return true;
            }
        }
 
        public SecurityStandardsManager StandardsManager
        {
            get
            {
                return _standardsManager;
            }
            set
            {
                ThrowIfImmutable();
                if (value == null)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("value"));
                }
                _standardsManager = value;
            }
        }
 
        public TimeSpan TimestampValidityDuration
        {
            get
            {
                return _timestampValidityDuration;
            }
            set
            {
                ThrowIfImmutable();
                if (value <= TimeSpan.Zero)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentOutOfRangeException("value", SRServiceModel.TimeSpanMustbeGreaterThanTimeSpanZero));
                }
                _timestampValidityDuration = value;
            }
        }
 
        public Uri ListenUri
        {
            get { return _listenUri; }
            set
            {
                ThrowIfImmutable();
                _listenUri = value;
            }
        }
 
        internal MessageSecurityVersion MessageSecurityVersion
        {
            get { return _messageSecurityVersion; }
        }
 
        // ISecurityCommunicationObject members
        public TimeSpan DefaultOpenTimeout
        {
            get { return ServiceDefaults.OpenTimeout; }
        }
 
        public TimeSpan DefaultCloseTimeout
        {
            get { return ServiceDefaults.CloseTimeout; }
        }
 
        public IAsyncResult OnBeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new OperationWithTimeoutAsyncResult(this.OnClose, timeout, callback, state);
        }
 
        public IAsyncResult OnBeginOpen(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return new OperationWithTimeoutAsyncResult(this.OnOpen, timeout, callback, state);
        }
 
        public void OnClosed()
        {
        }
 
        public void OnClosing()
        {
        }
 
        public void OnEndClose(IAsyncResult result)
        {
            OperationWithTimeoutAsyncResult.End(result);
        }
 
        public void OnEndOpen(IAsyncResult result)
        {
            OperationWithTimeoutAsyncResult.End(result);
        }
 
        public void OnFaulted()
        {
        }
 
        public void OnOpened()
        {
        }
 
        public void OnOpening()
        {
        }
 
        public virtual void OnAbort()
        {
            if (!_actAsInitiator)
            {
                foreach (SupportingTokenAuthenticatorSpecification spec in _channelSupportingTokenAuthenticatorSpecification)
                {
                    SecurityUtils.AbortTokenAuthenticatorIfRequired(spec.TokenAuthenticator);
                }
                foreach (string action in _scopedSupportingTokenAuthenticatorSpecification.Keys)
                {
                    ICollection<SupportingTokenAuthenticatorSpecification> supportingAuthenticators = _scopedSupportingTokenAuthenticatorSpecification[action];
                    foreach (SupportingTokenAuthenticatorSpecification spec in supportingAuthenticators)
                    {
                        SecurityUtils.AbortTokenAuthenticatorIfRequired(spec.TokenAuthenticator);
                    }
                }
            }
        }
 
        public virtual void OnClose(TimeSpan timeout)
        {
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            if (!_actAsInitiator)
            {
                foreach (SupportingTokenAuthenticatorSpecification spec in _channelSupportingTokenAuthenticatorSpecification)
                {
                    SecurityUtils.CloseTokenAuthenticatorIfRequired(spec.TokenAuthenticator, timeoutHelper.RemainingTime());
                }
                foreach (string action in _scopedSupportingTokenAuthenticatorSpecification.Keys)
                {
                    ICollection<SupportingTokenAuthenticatorSpecification> supportingAuthenticators = _scopedSupportingTokenAuthenticatorSpecification[action];
                    foreach (SupportingTokenAuthenticatorSpecification spec in supportingAuthenticators)
                    {
                        SecurityUtils.CloseTokenAuthenticatorIfRequired(spec.TokenAuthenticator, timeoutHelper.RemainingTime());
                    }
                }
            }
        }
 
        public virtual object CreateListenerSecurityState()
        {
            return null;
        }
 
        public SecurityProtocol CreateSecurityProtocol(EndpointAddress target, Uri via, object listenerSecurityState, bool isReturnLegSecurityRequired, TimeSpan timeout)
        {
            ThrowIfNotOpen();
            SecurityProtocol securityProtocol = OnCreateSecurityProtocol(target, via, listenerSecurityState, timeout);
            if (securityProtocol == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MessageSecurityException(SRServiceModel.ProtocolFactoryCouldNotCreateProtocol));
            }
            return securityProtocol;
        }
 
        public virtual EndpointIdentity GetIdentityOfSelf()
        {
            return null;
        }
 
        public virtual T GetProperty<T>()
        {
            if (typeof(T) == typeof(Collection<ISecurityContextSecurityTokenCache>))
            {
                ThrowIfNotOpen();
                Collection<ISecurityContextSecurityTokenCache> result = new Collection<ISecurityContextSecurityTokenCache>();
                if (_channelSupportingTokenAuthenticatorSpecification != null)
                {
                    foreach (SupportingTokenAuthenticatorSpecification spec in _channelSupportingTokenAuthenticatorSpecification)
                    {
                        if (spec.TokenAuthenticator is ISecurityContextSecurityTokenCacheProvider)
                        {
                            result.Add(((ISecurityContextSecurityTokenCacheProvider)spec.TokenAuthenticator).TokenCache);
                        }
                    }
                }
                return (T)(object)(result);
            }
            else
            {
                return default(T);
            }
        }
 
        protected abstract SecurityProtocol OnCreateSecurityProtocol(EndpointAddress target, Uri via, object listenerSecurityState, TimeSpan timeout);
 
        private void VerifyTypeUniqueness(ICollection<SupportingTokenAuthenticatorSpecification> supportingTokenAuthenticators)
        {
            // its ok to go brute force here since we are dealing with a small number of authenticators
            foreach (SupportingTokenAuthenticatorSpecification spec in supportingTokenAuthenticators)
            {
                Type authenticatorType = spec.TokenAuthenticator.GetType();
                int numSkipped = 0;
                foreach (SupportingTokenAuthenticatorSpecification spec2 in supportingTokenAuthenticators)
                {
                    Type spec2AuthenticatorType = spec2.TokenAuthenticator.GetType();
                    if (object.ReferenceEquals(spec, spec2))
                    {
                        if (numSkipped > 0)
                        {
                            throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(string.Format(SRServiceModel.MultipleSupportingAuthenticatorsOfSameType, spec.TokenParameters.GetType())));
                        }
                        ++numSkipped;
                        continue;
                    }
                    else if (authenticatorType.IsAssignableFrom(spec2AuthenticatorType) || spec2AuthenticatorType.IsAssignableFrom(authenticatorType))
                    {
                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(string.Format(SRServiceModel.MultipleSupportingAuthenticatorsOfSameType, spec.TokenParameters.GetType())));
                    }
                }
            }
        }
 
        internal IList<SupportingTokenAuthenticatorSpecification> GetSupportingTokenAuthenticators(string action, out bool expectSignedTokens, out bool expectBasicTokens, out bool expectEndorsingTokens)
        {
            if (_mergedSupportingTokenAuthenticatorsMap != null && _mergedSupportingTokenAuthenticatorsMap.Count > 0)
            {
                if (action != null && _mergedSupportingTokenAuthenticatorsMap.ContainsKey(action))
                {
                    MergedSupportingTokenAuthenticatorSpecification mergedSpec = _mergedSupportingTokenAuthenticatorsMap[action];
                    expectSignedTokens = mergedSpec.ExpectSignedTokens;
                    expectBasicTokens = mergedSpec.ExpectBasicTokens;
                    expectEndorsingTokens = mergedSpec.ExpectEndorsingTokens;
                    return mergedSpec.SupportingTokenAuthenticators;
                }
                else if (_mergedSupportingTokenAuthenticatorsMap.ContainsKey(MessageHeaders.WildcardAction))
                {
                    MergedSupportingTokenAuthenticatorSpecification mergedSpec = _mergedSupportingTokenAuthenticatorsMap[MessageHeaders.WildcardAction];
                    expectSignedTokens = mergedSpec.ExpectSignedTokens;
                    expectBasicTokens = mergedSpec.ExpectBasicTokens;
                    expectEndorsingTokens = mergedSpec.ExpectEndorsingTokens;
                    return mergedSpec.SupportingTokenAuthenticators;
                }
            }
            expectSignedTokens = _expectChannelSignedTokens;
            expectBasicTokens = _expectChannelBasicTokens;
            expectEndorsingTokens = _expectChannelEndorsingTokens;
            // in case the channelSupportingTokenAuthenticators is empty return null so that its Count does not get accessed.
            return (Object.ReferenceEquals(_channelSupportingTokenAuthenticatorSpecification, EmptyTokenAuthenticators)) ? null : (IList<SupportingTokenAuthenticatorSpecification>)_channelSupportingTokenAuthenticatorSpecification;
        }
 
        private void MergeSupportingTokenAuthenticators(TimeSpan timeout)
        {
            if (_scopedSupportingTokenAuthenticatorSpecification.Count == 0)
            {
                _mergedSupportingTokenAuthenticatorsMap = null;
            }
            else
            {
                TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
                _expectSupportingTokens = true;
                _mergedSupportingTokenAuthenticatorsMap = new Dictionary<string, MergedSupportingTokenAuthenticatorSpecification>();
                foreach (string action in _scopedSupportingTokenAuthenticatorSpecification.Keys)
                {
                    ICollection<SupportingTokenAuthenticatorSpecification> scopedAuthenticators = _scopedSupportingTokenAuthenticatorSpecification[action];
                    if (scopedAuthenticators == null || scopedAuthenticators.Count == 0)
                    {
                        continue;
                    }
                    Collection<SupportingTokenAuthenticatorSpecification> mergedAuthenticators = new Collection<SupportingTokenAuthenticatorSpecification>();
                    bool expectSignedTokens = _expectChannelSignedTokens;
                    bool expectBasicTokens = _expectChannelBasicTokens;
                    bool expectEndorsingTokens = _expectChannelEndorsingTokens;
                    foreach (SupportingTokenAuthenticatorSpecification spec in _channelSupportingTokenAuthenticatorSpecification)
                    {
                        mergedAuthenticators.Add(spec);
                    }
                    foreach (SupportingTokenAuthenticatorSpecification spec in scopedAuthenticators)
                    {
                        SecurityUtils.OpenTokenAuthenticatorIfRequired(spec.TokenAuthenticator, timeoutHelper.RemainingTime());
                        mergedAuthenticators.Add(spec);
                        if (spec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.Endorsing ||
                            spec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.SignedEndorsing)
                        {
                            if (spec.TokenParameters.RequireDerivedKeys && !spec.TokenParameters.HasAsymmetricKey)
                            {
                                _expectKeyDerivation = true;
                            }
                        }
                        SecurityTokenAttachmentMode mode = spec.SecurityTokenAttachmentMode;
                        if (mode == SecurityTokenAttachmentMode.SignedEncrypted
                            || mode == SecurityTokenAttachmentMode.Signed
                            || mode == SecurityTokenAttachmentMode.SignedEndorsing)
                        {
                            expectSignedTokens = true;
                            if (mode == SecurityTokenAttachmentMode.SignedEncrypted)
                            {
                                expectBasicTokens = true;
                            }
                        }
                        if (mode == SecurityTokenAttachmentMode.Endorsing || mode == SecurityTokenAttachmentMode.SignedEndorsing)
                        {
                            expectEndorsingTokens = true;
                        }
                    }
                    VerifyTypeUniqueness(mergedAuthenticators);
                    MergedSupportingTokenAuthenticatorSpecification mergedSpec = new MergedSupportingTokenAuthenticatorSpecification();
                    mergedSpec.SupportingTokenAuthenticators = mergedAuthenticators;
                    mergedSpec.ExpectBasicTokens = expectBasicTokens;
                    mergedSpec.ExpectEndorsingTokens = expectEndorsingTokens;
                    mergedSpec.ExpectSignedTokens = expectSignedTokens;
                    _mergedSupportingTokenAuthenticatorsMap.Add(action, mergedSpec);
                }
            }
        }
 
        protected RecipientServiceModelSecurityTokenRequirement CreateRecipientSecurityTokenRequirement()
        {
            RecipientServiceModelSecurityTokenRequirement requirement = new RecipientServiceModelSecurityTokenRequirement();
            requirement.SecurityBindingElement = _securityBindingElement;
            requirement.SecurityAlgorithmSuite = this.IncomingAlgorithmSuite;
            requirement.ListenUri = _listenUri;
            requirement.MessageSecurityVersion = this.MessageSecurityVersion.SecurityTokenVersion;
            requirement.Properties[ServiceModelSecurityTokenRequirement.ExtendedProtectionPolicy] = _extendedProtectionPolicy;
            return requirement;
        }
 
        private RecipientServiceModelSecurityTokenRequirement CreateRecipientSecurityTokenRequirement(SecurityTokenParameters parameters, SecurityTokenAttachmentMode attachmentMode)
        {
            RecipientServiceModelSecurityTokenRequirement requirement = CreateRecipientSecurityTokenRequirement();
 
            requirement.KeyUsage = SecurityKeyUsage.Signature;
            requirement.Properties[ServiceModelSecurityTokenRequirement.MessageDirectionProperty] = MessageDirection.Input;
            requirement.Properties[ServiceModelSecurityTokenRequirement.SupportingTokenAttachmentModeProperty] = attachmentMode;
            requirement.Properties[ServiceModelSecurityTokenRequirement.ExtendedProtectionPolicy] = _extendedProtectionPolicy;
            return requirement;
        }
 
        private void AddSupportingTokenAuthenticators(SupportingTokenParameters supportingTokenParameters, bool isOptional, IList<SupportingTokenAuthenticatorSpecification> authenticatorSpecList)
        {
            for (int i = 0; i < supportingTokenParameters.Endorsing.Count; ++i)
            {
                SecurityTokenRequirement requirement = this.CreateRecipientSecurityTokenRequirement(supportingTokenParameters.Endorsing[i], SecurityTokenAttachmentMode.Endorsing);
                try
                {
                    System.IdentityModel.Selectors.SecurityTokenResolver resolver;
                    System.IdentityModel.Selectors.SecurityTokenAuthenticator authenticator = this.SecurityTokenManager.CreateSecurityTokenAuthenticator(requirement, out resolver);
                    SupportingTokenAuthenticatorSpecification authenticatorSpec = new SupportingTokenAuthenticatorSpecification(authenticator, resolver, SecurityTokenAttachmentMode.Endorsing, supportingTokenParameters.Endorsing[i], isOptional);
                    authenticatorSpecList.Add(authenticatorSpec);
                }
                catch (Exception e)
                {
                    if (!isOptional || Fx.IsFatal(e))
                    {
                        throw;
                    }
                }
            }
            for (int i = 0; i < supportingTokenParameters.SignedEndorsing.Count; ++i)
            {
                SecurityTokenRequirement requirement = this.CreateRecipientSecurityTokenRequirement(supportingTokenParameters.SignedEndorsing[i], SecurityTokenAttachmentMode.SignedEndorsing);
                try
                {
                    System.IdentityModel.Selectors.SecurityTokenResolver resolver;
                    System.IdentityModel.Selectors.SecurityTokenAuthenticator authenticator = this.SecurityTokenManager.CreateSecurityTokenAuthenticator(requirement, out resolver);
                    SupportingTokenAuthenticatorSpecification authenticatorSpec = new SupportingTokenAuthenticatorSpecification(authenticator, resolver, SecurityTokenAttachmentMode.SignedEndorsing, supportingTokenParameters.SignedEndorsing[i], isOptional);
                    authenticatorSpecList.Add(authenticatorSpec);
                }
                catch (Exception e)
                {
                    if (!isOptional || Fx.IsFatal(e))
                    {
                        throw;
                    }
                }
            }
            for (int i = 0; i < supportingTokenParameters.SignedEncrypted.Count; ++i)
            {
                SecurityTokenRequirement requirement = this.CreateRecipientSecurityTokenRequirement(supportingTokenParameters.SignedEncrypted[i], SecurityTokenAttachmentMode.SignedEncrypted);
                try
                {
                    System.IdentityModel.Selectors.SecurityTokenResolver resolver;
                    System.IdentityModel.Selectors.SecurityTokenAuthenticator authenticator = this.SecurityTokenManager.CreateSecurityTokenAuthenticator(requirement, out resolver);
                    SupportingTokenAuthenticatorSpecification authenticatorSpec = new SupportingTokenAuthenticatorSpecification(authenticator, resolver, SecurityTokenAttachmentMode.SignedEncrypted, supportingTokenParameters.SignedEncrypted[i], isOptional);
                    authenticatorSpecList.Add(authenticatorSpec);
                }
                catch (Exception e)
                {
                    if (!isOptional || Fx.IsFatal(e))
                    {
                        throw;
                    }
                }
            }
            for (int i = 0; i < supportingTokenParameters.Signed.Count; ++i)
            {
                SecurityTokenRequirement requirement = this.CreateRecipientSecurityTokenRequirement(supportingTokenParameters.Signed[i], SecurityTokenAttachmentMode.Signed);
                try
                {
                    System.IdentityModel.Selectors.SecurityTokenResolver resolver;
                    System.IdentityModel.Selectors.SecurityTokenAuthenticator authenticator = this.SecurityTokenManager.CreateSecurityTokenAuthenticator(requirement, out resolver);
                    SupportingTokenAuthenticatorSpecification authenticatorSpec = new SupportingTokenAuthenticatorSpecification(authenticator, resolver, SecurityTokenAttachmentMode.Signed, supportingTokenParameters.Signed[i], isOptional);
                    authenticatorSpecList.Add(authenticatorSpec);
                }
                catch (Exception e)
                {
                    if (!isOptional || Fx.IsFatal(e))
                    {
                        throw;
                    }
                }
            }
        }
 
        public virtual void OnOpen(TimeSpan timeout)
        {
            if (this.SecurityBindingElement == null)
            {
                this.OnPropertySettingsError("SecurityBindingElement", true);
            }
            if (this.SecurityTokenManager == null)
            {
                this.OnPropertySettingsError("SecurityTokenManager", true);
            }
            _messageSecurityVersion = _standardsManager.MessageSecurityVersion;
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            _expectOutgoingMessages = this.ActAsInitiator || this.SupportsRequestReply;
            _expectIncomingMessages = !this.ActAsInitiator || this.SupportsRequestReply;
            if (!_actAsInitiator)
            {
                AddSupportingTokenAuthenticators(_securityBindingElement.EndpointSupportingTokenParameters, false, (IList<SupportingTokenAuthenticatorSpecification>)_channelSupportingTokenAuthenticatorSpecification);
                // validate the token authenticator types and create a merged map if needed.
                if (!_channelSupportingTokenAuthenticatorSpecification.IsReadOnly)
                {
                    if (_channelSupportingTokenAuthenticatorSpecification.Count == 0)
                    {
                        _channelSupportingTokenAuthenticatorSpecification = EmptyTokenAuthenticators;
                    }
                    else
                    {
                        _expectSupportingTokens = true;
                        foreach (SupportingTokenAuthenticatorSpecification tokenAuthenticatorSpec in _channelSupportingTokenAuthenticatorSpecification)
                        {
                            SecurityUtils.OpenTokenAuthenticatorIfRequired(tokenAuthenticatorSpec.TokenAuthenticator, timeoutHelper.RemainingTime());
                            if (tokenAuthenticatorSpec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.Endorsing
                                || tokenAuthenticatorSpec.SecurityTokenAttachmentMode == SecurityTokenAttachmentMode.SignedEndorsing)
                            {
                                if (tokenAuthenticatorSpec.TokenParameters.RequireDerivedKeys && !tokenAuthenticatorSpec.TokenParameters.HasAsymmetricKey)
                                {
                                    _expectKeyDerivation = true;
                                }
                            }
                            SecurityTokenAttachmentMode mode = tokenAuthenticatorSpec.SecurityTokenAttachmentMode;
                            if (mode == SecurityTokenAttachmentMode.SignedEncrypted
                                || mode == SecurityTokenAttachmentMode.Signed
                                || mode == SecurityTokenAttachmentMode.SignedEndorsing)
                            {
                                _expectChannelSignedTokens = true;
                                if (mode == SecurityTokenAttachmentMode.SignedEncrypted)
                                {
                                    _expectChannelBasicTokens = true;
                                }
                            }
                            if (mode == SecurityTokenAttachmentMode.Endorsing || mode == SecurityTokenAttachmentMode.SignedEndorsing)
                            {
                                _expectChannelEndorsingTokens = true;
                            }
                        }
                        _channelSupportingTokenAuthenticatorSpecification =
                            new ReadOnlyCollection<SupportingTokenAuthenticatorSpecification>((Collection<SupportingTokenAuthenticatorSpecification>)_channelSupportingTokenAuthenticatorSpecification);
                    }
                }
                VerifyTypeUniqueness(_channelSupportingTokenAuthenticatorSpecification);
                MergeSupportingTokenAuthenticators(timeoutHelper.RemainingTime());
            }
 
            if (this.DetectReplays)
            {
                if (!this.SupportsReplayDetection)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("DetectReplays", string.Format(SRServiceModel.SecurityProtocolCannotDoReplayDetection, this));
                }
                if (this.MaxClockSkew == TimeSpan.MaxValue || this.ReplayWindow == TimeSpan.MaxValue)
                {
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SRServiceModel.NoncesCachedInfinitely));
                }
 
                // If DetectReplays is true and nonceCache is null then use the default InMemoryNonceCache. 
                if (_nonceCache == null)
                {
                    // The nonce needs to be cached for replayWindow + 2*clockSkew to eliminate replays
                    _nonceCache = new InMemoryNonceCache(this.ReplayWindow + this.MaxClockSkew + this.MaxClockSkew, this.MaxCachedNonces);
                }
            }
 
            _derivedKeyTokenAuthenticator = new NonValidatingSecurityTokenAuthenticator<DerivedKeySecurityToken>();
        }
 
        public void Open(bool actAsInitiator, TimeSpan timeout)
        {
            _actAsInitiator = actAsInitiator;
            _communicationObject.Open(timeout);
        }
 
        public IAsyncResult BeginOpen(bool actAsInitiator, TimeSpan timeout, AsyncCallback callback, object state)
        {
            _actAsInitiator = actAsInitiator;
            return this.CommunicationObject.BeginOpen(timeout, callback, state);
        }
 
        public void EndOpen(IAsyncResult result)
        {
            this.CommunicationObject.EndOpen(result);
        }
 
        public void Close(bool aborted, TimeSpan timeout)
        {
            if (aborted)
            {
                this.CommunicationObject.Abort();
            }
            else
            {
                this.CommunicationObject.Close(timeout);
            }
        }
 
        public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state)
        {
            return this.CommunicationObject.BeginClose(timeout, callback, state);
        }
 
        public void EndClose(IAsyncResult result)
        {
            this.CommunicationObject.EndClose(result);
        }
 
        internal void Open(string propertyName, bool requiredForForwardDirection, SecurityTokenAuthenticator authenticator, TimeSpan timeout)
        {
            if (authenticator != null)
            {
                SecurityUtils.OpenTokenAuthenticatorIfRequired(authenticator, timeout);
            }
            else
            {
                OnPropertySettingsError(propertyName, requiredForForwardDirection);
            }
        }
 
        internal void Open(string propertyName, bool requiredForForwardDirection, SecurityTokenProvider provider, TimeSpan timeout)
        {
            if (provider != null)
            {
                SecurityUtils.OpenTokenProviderIfRequired(provider, timeout);
            }
            else
            {
                OnPropertySettingsError(propertyName, requiredForForwardDirection);
            }
        }
 
        internal void OnPropertySettingsError(string propertyName, bool requiredForForwardDirection)
        {
            if (requiredForForwardDirection)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(
                    string.Format(SRServiceModel.PropertySettingErrorOnProtocolFactory, propertyName, this),
                    propertyName));
            }
            else if (_requestReplyErrorPropertyName == null)
            {
                _requestReplyErrorPropertyName = propertyName;
            }
        }
 
        private void ThrowIfReturnDirectionSecurityNotSupported()
        {
            if (_requestReplyErrorPropertyName != null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(
                    string.Format(SRServiceModel.PropertySettingErrorOnProtocolFactory, _requestReplyErrorPropertyName, this),
                    _requestReplyErrorPropertyName));
            }
        }
 
        internal void ThrowIfImmutable()
        {
            _communicationObject.ThrowIfDisposedOrImmutable();
        }
 
        private void ThrowIfNotOpen()
        {
            _communicationObject.ThrowIfNotOpened();
        }
    }
 
    internal struct MergedSupportingTokenAuthenticatorSpecification
    {
        public Collection<SupportingTokenAuthenticatorSpecification> SupportingTokenAuthenticators;
        public bool ExpectSignedTokens;
        public bool ExpectEndorsingTokens;
        public bool ExpectBasicTokens;
    }
}