File: System\ServiceModel\Security\InitiatorSessionSymmetricTransportSecurityProtocol.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.IdentityModel.Selectors;
    using System.IdentityModel.Tokens;
    using System.Runtime;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Security.Tokens;
 
    sealed class InitiatorSessionSymmetricTransportSecurityProtocol : TransportSecurityProtocol, IInitiatorSecuritySessionProtocol
    {
        SecurityToken outgoingSessionToken;
        List<SecurityToken> incomingSessionTokens;
        Object thisLock = new Object();
        DerivedKeySecurityToken derivedSignatureToken;
        bool requireDerivedKeys;
 
        public InitiatorSessionSymmetricTransportSecurityProtocol(SessionSymmetricTransportSecurityProtocolFactory factory,
            EndpointAddress target, Uri via) : base(factory, target, via)
        {
            if (factory.ActAsInitiator != true)
            {
                Fx.Assert("This protocol can only be used at the initiator.");
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.ProtocolMustBeInitiator, "InitiatorSessionSymmetricTransportSecurityProtocol")));
            }
            this.requireDerivedKeys = factory.SecurityTokenParameters.RequireDerivedKeys;
        }
 
        SessionSymmetricTransportSecurityProtocolFactory Factory
        {
            get { return (SessionSymmetricTransportSecurityProtocolFactory)this.SecurityProtocolFactory; }
        }
 
        Object ThisLock
        {
            get
            {
                return thisLock;
            }
        }
 
        public bool ReturnCorrelationState 
        {
            get
            {
                return false;
            }
            set
            {
            }
        }
 
        public SecurityToken GetOutgoingSessionToken()
        {
            lock (ThisLock)
            {
                return this.outgoingSessionToken;
            }
        }
 
        public void SetIdentityCheckAuthenticator(SecurityTokenAuthenticator authenticator)
        {
        }
 
        public void SetOutgoingSessionToken(SecurityToken token)
        {
            if (token == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("token");
            }
            lock (ThisLock)
            {
                this.outgoingSessionToken = token;
                if (this.requireDerivedKeys)
                {
                    string derivationAlgorithm = SecurityUtils.GetKeyDerivationAlgorithm(this.Factory.MessageSecurityVersion.SecureConversationVersion);
 
                    this.derivedSignatureToken = new DerivedKeySecurityToken(-1, 0,
                        this.Factory.OutgoingAlgorithmSuite.GetSignatureKeyDerivationLength(token, this.Factory.MessageSecurityVersion.SecureConversationVersion), null, DerivedKeySecurityToken.DefaultNonceLength, token, this.Factory.SecurityTokenParameters.CreateKeyIdentifierClause(token, SecurityTokenReferenceStyle.Internal), derivationAlgorithm, SecurityUtils.GenerateId());
                }
            }
        }
 
        public List<SecurityToken> GetIncomingSessionTokens()
        {
            lock (ThisLock)
            {
                return this.incomingSessionTokens;
            }
        }
 
        public void SetIncomingSessionTokens(List<SecurityToken> tokens)
        {
            if (tokens == null)
            {
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("tokens");
            }
            lock (ThisLock)
            {
                this.incomingSessionTokens = new List<SecurityToken>(tokens);
            }
        }
 
        void GetTokensForOutgoingMessages(out SecurityToken signingToken, out SecurityToken sourceToken, out SecurityTokenParameters tokenParameters)
        {
            lock (ThisLock)
            {
                if (this.requireDerivedKeys)
                {
                    signingToken = this.derivedSignatureToken;
                    sourceToken = this.outgoingSessionToken;
                }
                else
                {
                    signingToken = this.outgoingSessionToken;
                    sourceToken = null;
                }
            }
            tokenParameters = this.Factory.GetTokenParameters();
        }
 
        internal void SetupDelayedSecurityExecution(string actor, ref Message message, SecurityToken signingToken, SecurityToken sourceToken, SecurityTokenParameters tokenParameters,
            IList<SupportingTokenSpecification> supportingTokens)
        {
            SendSecurityHeader securityHeader = CreateSendSecurityHeaderForTransportProtocol(message, actor, this.Factory);
            securityHeader.RequireMessageProtection = false;
            if (sourceToken != null)
            {
                securityHeader.AddPrerequisiteToken(sourceToken);
            }
            AddSupportingTokens(securityHeader, supportingTokens);
            securityHeader.AddEndorsingSupportingToken(signingToken, tokenParameters);
            message = securityHeader.SetupExecution();
        }
 
        protected override void SecureOutgoingMessageAtInitiator(ref Message message, string actor, TimeSpan timeout)
        {
            SecurityToken signingToken;
            SecurityToken sourceToken;
            SecurityTokenParameters tokenParameters;
            this.GetTokensForOutgoingMessages(out signingToken, out sourceToken, out tokenParameters);
            IList<SupportingTokenSpecification> supportingTokens;
            this.TryGetSupportingTokens(this.SecurityProtocolFactory, this.Target, this.Via, message, timeout, true, out supportingTokens);
            SetupDelayedSecurityExecution(actor, ref message, signingToken, sourceToken, tokenParameters, supportingTokens);
        }
 
        protected override IAsyncResult BeginSecureOutgoingMessageAtInitiatorCore(Message message, string actor, TimeSpan timeout, AsyncCallback callback, object state)
        {
            SecurityToken signingToken;
            SecurityToken sourceToken;
            SecurityTokenParameters tokenParameters;
            this.GetTokensForOutgoingMessages(out signingToken, out sourceToken, out tokenParameters);
            IList<SupportingTokenSpecification> supportingTokens;
            TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
            if (!TryGetSupportingTokens(this.SecurityProtocolFactory, this.Target, this.Via, message, timeoutHelper.RemainingTime(), false, out supportingTokens))
            {
                return new SecureOutgoingMessageAsyncResult(actor, message, this, signingToken, sourceToken, tokenParameters, timeoutHelper.RemainingTime(), callback, state);
            }
            else
            {
                SetupDelayedSecurityExecution(actor, ref message, signingToken, sourceToken, tokenParameters, supportingTokens);
                return new CompletedAsyncResult<Message>(message, callback, state);
            }
        }
 
        protected override Message EndSecureOutgoingMessageAtInitiatorCore(IAsyncResult result)
        {
            if (result is CompletedAsyncResult<Message>)
            {
                return CompletedAsyncResult<Message>.End(result);
            }
            else
            {
                return SecureOutgoingMessageAsyncResult.End(result);
            }
        }
 
        sealed class SecureOutgoingMessageAsyncResult : GetSupportingTokensAsyncResult
        {
            Message message;
            string actor;
            SecurityToken signingToken;
            SecurityToken sourceToken;
            SecurityTokenParameters tokenParameters;
            InitiatorSessionSymmetricTransportSecurityProtocol binding;
 
            public SecureOutgoingMessageAsyncResult(string actor, Message message, InitiatorSessionSymmetricTransportSecurityProtocol binding, SecurityToken signingToken, SecurityToken sourceToken, SecurityTokenParameters tokenParameters, TimeSpan timeout, AsyncCallback callback, object state)
                : base(message, binding, timeout, callback, state)
            {
                this.actor = actor;
                this.message = message;
                this.binding = binding;
                this.signingToken = signingToken;
                this.sourceToken = sourceToken;
                this.tokenParameters = tokenParameters;
                this.Start();
            }
 
            protected override bool OnGetSupportingTokensDone(TimeSpan timeout)
            {
                this.binding.SetupDelayedSecurityExecution(actor, ref message, signingToken, sourceToken, tokenParameters, this.SupportingTokens);
                return true;
            }
 
            internal static Message End(IAsyncResult result)
            {
                SecureOutgoingMessageAsyncResult self = AsyncResult.End<SecureOutgoingMessageAsyncResult>(result);
                return self.message;
            }
        }
    }
}