
#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.Collections;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;

namespace MyCompany.Server.EncryptionSink
{
    /// <summary>
    /// Custom sink for encrypting or decrypting the outstream or instream messages.
    /// </summary>
    /// <remarks>
    /// <b>EncryptionServerSink</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>EncryptionServerSink</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
    ///     IServerChannelSinkProvider newProvider = new EncryptionServerSinkProvider(properties, null);
    ///     newProvider.Next = new APDUServerFormatterSinkProvider();  
    ///     
    ///     APDUServerChannel channel = new APDUServerChannel(newprovider, 0x7867);            
    ///     // register the communication channel
    ///     ChannelServices.RegisterChannel(channel);
    /// </code>
    /// Following example illustrates how to use <b>EncryptionServerSink</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   
    ///     IServerChannelSinkProvider newProvider = new EncryptionServerSinkProvider(properties, null);
    ///     newProvider.Next = new APDUServerFormatterSinkProvider();  
    ///		
    ///     APDUServerChannel channel = new APDUServerChannel(0x8765, newProvider);            
    ///     // 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 EncryptionServerSink: IServerChannelSink
	{
		private IServerChannelSink _nextSink;
		private byte[] _encryptionKey;
		private String _encryptionAlgorithm;
        private InputOutputStreamProcessorDelegate _outputStreamDelegate;
        private InputOutputStreamProcessorDelegate _inputStreamDelegate;

        /// <summary>
        /// Initializes a new instance of <see cref="EncryptionServerSink"/> 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 EncryptionServerSink(IServerChannelSink next, byte[] encryptionKey,String encryptionAlgorithm,
                                    InputOutputStreamProcessorDelegate inputStreamDelegate,
                                    InputOutputStreamProcessorDelegate outputStreamDelegate)
		{
			_encryptionKey = encryptionKey;
			_encryptionAlgorithm = encryptionAlgorithm;
			_nextSink = next;
            _inputStreamDelegate  = inputStreamDelegate;
            _outputStreamDelegate = outputStreamDelegate;
		}

        /// <summary>
        /// Gets an IDictionary of properties for the current channel sink.
        /// </summary>
        /// <value>Returns null.</value>
        public IDictionary Properties { get { return null ; } }

        /// <summary>
        /// /// Requests message processing from the current sink.
        /// </summary>
        /// <param name="sinkStack">A stack of channel sinks that called the current sink. </param>
        /// <param name="requestMsg">The message that contains the request. </param>
        /// <param name="requestHeaders">Headers retrieved from the incoming message from the client. </param>
        /// <param name="requestStream">The stream that needs to be to processed and passed on to the deserialization sink. 
        /// </param>
        /// <param name="responseMsg">When this method returns, contains an IMessage that holds the response message. This parameter is passed uninitialized. </param>
        /// <param name="responseHeaders">When this method returns, contains an ITransportHeaders that holds the headers that are to be added to return message heading to the client. This parameter is passed uninitialized. 
        ///  </param>
        /// <param name="responseStream">When this method returns, contains a Stream that is heading back to the transport sink. This parameter is passed uninitialized.</param>
        /// <returns>A ServerProcessing status value that provides information about how message was processed.
        /// </returns>      
		public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, 
			IMessage requestMsg, 
			ITransportHeaders requestHeaders,
			Stream requestStream, 
			out IMessage responseMsg, 
			out ITransportHeaders responseHeaders, 
			out Stream responseStream) 
		{

            if(_inputStreamDelegate != null)
            {
                requestStream = (MemoryStream)_inputStreamDelegate((MemoryStream)requestStream);
            }
            else
            {
                requestStream = EncryptionHelper.ProcessInboundStream(
                    requestStream,_encryptionAlgorithm, 
                    _encryptionKey);
            }
		
			ServerProcessing srvProc = _nextSink.ProcessMessage(sinkStack,
				requestMsg,
				requestHeaders,
				requestStream,
				out responseMsg,
				out responseHeaders,
				out responseStream);

			
            if(_outputStreamDelegate != null)
            {
                responseStream = (MemoryStream)_outputStreamDelegate((MemoryStream)responseStream);
            }
            else
            {
                responseStream = EncryptionHelper.ProcessOutboundStream(responseStream,
                                                       _encryptionAlgorithm,_encryptionKey); 
            }
				
			// returning status information
			return srvProc;
		}

        /// <summary>
        /// Returns the Stream onto which the provided message is to be serialized. 
        /// </summary>
        /// <param name="sinkStack">A stack of channel sinks that called the current sink. </param>
        /// <param name="state">The state that has been pushed to the stack by this sink. 
        /// </param>
        /// <param name="msg">The response message to serialize.</param>
        /// <param name="headers">The headers to put in the response stream to the client.</param>
        /// <returns>returns null for this implementation.</returns>
		public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, 
			object state, 
			IMessage msg, 
			ITransportHeaders headers)
		{
			return null;
		}

        /// <summary>
        /// Gets the next server channel sink in the server sink chain.
        /// </summary>
        /// <value>The next server channel sink in the server sink chain.</value>
        /// <remarks>
        /// Channel sinks are linked together in a chain of sink providers, and all channel messages flow through this chain before the message is serialized and transported.
        /// </remarks>
		public IServerChannelSink NextChannelSink 
		{
			get 
			{
				return _nextSink;
			}
		}

        /// <summary>
        /// Requests processing from the current sink of the response from a method call sent asynchronously.
        /// </summary>
        /// 
        /// <remarks>This method is provided for compilation purposes and throws NotImplementedException</remarks>
        /// 
        /// <param name="sinkStack">A stack of sinks leading back to the server transport sink.</param>
        /// <param name="state">Information generated on the request side that is associated with this sink.</param>
        /// <param name="msg">The response message.</param>
        /// <param name="headers">The headers to add to the return message heading to the client.</param>
        /// <param name="stream">The stream heading back to the transport sink.</param>
        public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers, Stream stream)
        {
            throw new NotImplementedException();
        }
	}
}

