
#region Copyright
// (c) Copyright Axalto Inc., 2006.                    
// All rights reserved.
//
// This file is part of the SmartCard.NET Framework SDK Code Samples.
//
// This source code is intended only as a supplement to Axalto
// Development Tools and/or on-line documentation.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
#endregion

using System;
using System.IO;
using System.Text;
using System.Security.Permissions;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Messaging;

namespace MyCompany.Client.EncryptionSink
{
    /// <summary>
    /// Custom sink for encrypting or decrypting the outstream or instream messages.
    /// </summary>
    /// <remarks>
    /// <b>EncryptionClientSink</b> is a custom sink which decrypts the message received
    /// from the card and dispatch it to next sink in the chain. Also the messages sent to the
    /// card are encrypted.
    /// </remarks>
    /// <example>
    /// Following example illustrates how to use <b>EncryptionClientSink</b> using the encryption
    /// and decryption algorithms (DES,TripleDES and Rinjdael) processors provided with sink.
    /// <code>
    ///     FileStream fs = new FileStream(keyfile,FileMode.Open);
    ///     byte[] encryptionKey = new Byte[fs.Length];
    ///     fs.Read(encryptionKey,0,encryptionKey.Length);
    ///     fs.Close();
    ///
    ///     // Set up the encryption sink properties.
    ///     Hashtable properties = new Hashtable();
    ///     properties["algorithm"] = algorithm;
    ///     properties["key"] = encryptionKey;            
    ///
    ///     // Create an encryption sink provider
    ///     IClientChannelSinkProvider provider = new APDUClientFormatterSinkProvider();
    ///     provider.Next = new EncryptionClientSinkProvider(properties);
	///		
    ///     APDUClientChannel channel = new APDUClientChannel("EncryptionSinkClient", provider);            
    ///     // register the communication channel
    ///     ChannelServices.RegisterChannel(channel);
    /// </code>
    /// Following example illustrates how to use <b>EncryptionClientSink</b> and provide the custom
    /// encryption and decryption algorithms using <see cref="InputOutputStreamProcessorDelegate"/>.
    /// <code>
    ///     InputOutputStreamProcessorDelegate inStreamDelegate = new InputOutputStreamProcessorDelegate(Process_InBoundStream);
    ///     InputOutputStreamProcessorDelegate outStreamDelegate = new InputOutputStreamProcessorDelegate(Process_OutBoundStream);    /// 
    /// 
    ///     FileStream fs = new FileStream(keyfile,FileMode.Open);
    ///     byte[] encryptionKey = new Byte[fs.Length];
    ///     fs.Read(encryptionKey,0,encryptionKey.Length);
    ///     fs.Close();
    ///
    ///     // Set up the encryption sink properties.
    ///     Hashtable properties = new Hashtable();
    ///     properties["algorithm"] = algorithm;
    ///     properties["key"] = encryptionKey;       
    ///     properties["_inStreamDelegate"] = inStreamDelegate;
    ///     properties["_outStreamDelegate"] = outStreamDelegate;     
    ///
    ///     // Create an encryption sink provider
    ///     IClientChannelSinkProvider provider = new APDUClientFormatterSinkProvider();
    ///     provider.Next = new EncryptionClientSinkProvider(properties);
    ///		
    ///     APDUClientChannel channel = new APDUClientChannel("EncryptionSinkClient", provider);            
    ///     // register the communication channel
    ///     ChannelServices.RegisterChannel(channel);    
    ///     
    ///     MemoryStream Process_InBoundStream(MemoryStream mem){
    ///         // use some custom cryptographic algorithm to decrypt the message strean <i>mem</i>
    ///     }
    ///     
    ///     MemoryStream Process_OutBoundStream(MemoryStream mem){
    ///         // use some custom cryptographic algorithm to encrypt the message strean <i>mem</i>
    ///     }
    ///     
    /// </code>
    /// </example>
	public class EncryptionClientSink: BaseChannelSinkWithProperties,IClientChannelSink
	{
		private IClientChannelSink _nextSink;
		private byte[] _encryptionKey;
		private String _encryptionAlgorithm;
        private InputOutputStreamProcessorDelegate _outputStreamDelegate;
        private InputOutputStreamProcessorDelegate _inputStreamDelegate;

        /// <summary>
        /// Initializes a new instance of <see cref="EncryptionClientSink"/> class.
        /// </summary>
        /// <param name="next">Next sink in the sink chain.</param>
        /// <param name="encryptionKey">Key to be used for Encryption or decryption.</param>
        /// <param name="encryptionAlgorithm">Encryption algorithm to use.</param>
        /// <param name="inputStreamDelegate">Method which will be invoked for performing decryption of message stream.</param>
        /// <param name="outputStreamDelegate">Method which will invoked for performing encryption of message stream.</param>
		public EncryptionClientSink(IClientChannelSink next, 
			        byte[] encryptionKey, String encryptionAlgorithm,
                    InputOutputStreamProcessorDelegate inputStreamDelegate,
                    InputOutputStreamProcessorDelegate outputStreamDelegate)
 
		{
			_nextSink = next;
			_encryptionKey = encryptionKey;
			_encryptionAlgorithm = encryptionAlgorithm;

            _inputStreamDelegate  = inputStreamDelegate;
            _outputStreamDelegate = outputStreamDelegate;
		}

        /// <summary>
        /// Requests message processing from the current sink.
        /// </summary>
        /// <param name="msg">The message to process.</param>
        /// <param name="requestHeaders">The headers to add to the outgoing message heading to the server. 
        /// </param>
        /// <param name="requestStream">The stream headed to the transport sink. </param>
        /// <param name="responseHeaders">When this method returns, contains an ITransportHeaders interface that holds the headers that the server returned. This parameter is passed uninitialized. 
        /// </param>
        /// <param name="responseStream">When this method returns, contains a Stream coming back from the transport sink. This parameter is passed uninitialized. 
        /// </param>
        [SecurityPermission(SecurityAction.LinkDemand, Infrastructure=true)]
		public void ProcessMessage(IMessage msg, 
			ITransportHeaders requestHeaders, 
			Stream requestStream, 
			out ITransportHeaders responseHeaders, 
			out Stream responseStream) 
		{		
            
            if(_outputStreamDelegate != null)
            {
                requestStream = (MemoryStream)_outputStreamDelegate((MemoryStream)requestStream);
            }
            else
            {
                requestStream = EncryptionHelper.ProcessOutboundStream(requestStream,
                    _encryptionAlgorithm,_encryptionKey);
            }

		    // forward the call to the next sink
			_nextSink.ProcessMessage(msg,
			                          requestHeaders,
				                      requestStream, 
				                      out responseHeaders, 
				                      out responseStream);

            
            if(_inputStreamDelegate != null)
            {
                responseStream = (MemoryStream)_inputStreamDelegate((MemoryStream)responseStream);
            }
            else
            {
                responseStream = EncryptionHelper.ProcessInboundStream(
                    responseStream,
                    _encryptionAlgorithm,
                    _encryptionKey); 
            }
		}

        /// <summary>
        /// Asynochronus messaging is not supported by <see cref="EncryptionClientSink"/>.
        /// </summary>        
		/// <param name="headers">The transport headers.</param>
		/// <param name="msg">The message</param>
		/// <param name="sinkStack">The sink stack on which the request is made.</param>
		/// <param name="stream">The stream.</param>
		[SecurityPermission(SecurityAction.LinkDemand, Infrastructure=true)]
		public void AsyncProcessRequest(IClientChannelSinkStack sinkStack, 
			IMessage msg, 
			ITransportHeaders headers, 
			Stream stream) 
		{
			throw new NotSupportedException();
		}

        /// <summary>
        /// Asynochronus messaging is not supported by <see cref="EncryptionClientSink"/>.
        /// </summary>        
		/// <param name="headers">The transport headers.</param>
		/// <param name="state">The state of request</param>
		/// <param name="sinkStack">The sink stack on which the request is made.</param>
		/// <param name="stream">The stream.</param>
		[SecurityPermission(SecurityAction.LinkDemand, Infrastructure=true)]
		public void AsyncProcessResponse(IClientResponseChannelSinkStack sinkStack, 
			object state, 
			ITransportHeaders headers, 
			Stream stream) 
		{
            throw new NotSupportedException();
		}


        /// <summary>
        /// Returns the Stream onto which the provided message is to be serialized. 
        /// </summary>
        /// <param name="msg">The IMethodCallMessage containing details about the method call.</param>
        /// <param name="headers">The headers to add to the outgoing message heading to the server. 
        /// </param>
        /// <returns>Returns always null for <see cref="EncryptionClientSink"/>.</returns>
		[SecurityPermission(SecurityAction.LinkDemand, Infrastructure=true)]
        public Stream GetRequestStream(IMessage msg, 
			ITransportHeaders headers) 
		{
			return null; // request stream will be manipulated later
		}

        /// <summary>
        /// Gets the next <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemruntimeremotingchannelsiclientchannelsinkclasstopic.asp">IClientChannelSink</a> in the sink chain.
        /// </summary>
        /// <value>The next <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemruntimeremotingchannelsiclientchannelsinkclasstopic.asp">IClientChannelSink</a>in the sink chain.</value>
		public IClientChannelSink NextChannelSink 
		{
            [SecurityPermission(SecurityAction.LinkDemand, Infrastructure=true)]
			get {
				return _nextSink;
			}
		}
	}
}

