Authentication Stream : Stream « File Stream « C# / C Sharp






Authentication Stream

  
// (c) Copyright slimCODE Software Inc. - www.slimcode.com
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace SlimCode.Utils
{
  internal class AuthenticationStream : Stream
  {
    public AuthenticationStream( Stream innerStream, byte[] authenticationKey, AuthenticationType authenticationType, bool writing )
    {
      if( innerStream == null )
        throw new ArgumentNullException( "innerStream" );

      if( writing && !innerStream.CanWrite )
        throw new ArgumentException( "Cannot write to inner stream.", "innerStream" );

      if( !writing && !innerStream.CanRead )
        throw new ArgumentException( "Cannot read from inner stream.", "innerStream" );

      if( authenticationKey == null )
        throw new ArgumentNullException( "authenticationKey" );

      if( authenticationKey.Length < 16 )
        throw new ArgumentException( "The authentication key cannot be less than 16 bytes." );

      switch( authenticationType )
      {
        case AuthenticationType.MD5:
          m_hmac = new HMACMD5( authenticationKey );
          break;

        case AuthenticationType.SHA1:
          m_hmac = new HMACSHA1( authenticationKey, true );
          break;

        case AuthenticationType.SHA256:
          m_hmac = new HMACSHA256( authenticationKey );
          break;

        case AuthenticationType.SHA384:
          m_hmac = new HMACSHA384( authenticationKey );
          break;

        case AuthenticationType.SHA512:
          m_hmac = new HMACSHA512( authenticationKey );
          break;

        default:
          throw new ArgumentException( "Unknown authentication type.", "authenticationType" );
      }

      m_innerStream = innerStream;
      m_writing = writing;
    }

    public override bool CanRead
    {
      get { return !m_writing; }
    }

    public override bool CanSeek
    {
      get { return false; }
    }

    public override bool CanWrite
    {
      get { return m_writing; }
    }

    public override void Flush()
    {
      m_innerStream.Flush();
    }

    public override long Length
    {
      get { return m_innerStream.Length; }
    }

    public override long Position
    {
      get { return m_innerStream.Position; }
      set { throw new NotSupportedException( "This stream does not support seeking." ); }
    }

    public override int Read( byte[] buffer, int offset, int count )
    {
      if( m_writing )
        throw new IOException( "Cannot read from this stream. It is open for writing." );

      if( buffer == null )
        throw new ArgumentNullException( "buffer" );

      if( ( offset < 0 ) || ( offset >= buffer.Length ) )
        throw new ArgumentOutOfRangeException( "offset" );

      if( count < 0 )
        throw new ArgumentOutOfRangeException( "count" );

      if( offset + count > buffer.Length )
        throw new ArgumentOutOfRangeException( "count" );

      if( count == 0 )
        return 0;

      int read = 0;
      int hashSize = m_hmac.HashSize / 8;

      if( m_bufferAvailable > hashSize )
      {
        read = ( count < ( m_bufferAvailable - hashSize ) ) ? ( count ) : ( m_bufferAvailable - hashSize );

        Array.Copy( m_buffer, 0, buffer, offset, read );

        m_hmac.TransformBlock( buffer, offset, read, buffer, offset );

        offset += read;
        count -= read;
        
        m_bufferAvailable -= read;

        Array.Copy( m_buffer, read, m_buffer, 0, m_bufferAvailable );
      }

      if( count > 0 )
      {
        int innerRead = m_innerStream.Read( m_buffer, m_bufferAvailable, m_buffer.Length - m_bufferAvailable );

        if( innerRead > 0 )
        {
          m_bufferAvailable += innerRead;

          read += this.Read( buffer, offset, count );
        }
      }

      return read;
    }

    public override long Seek( long offset, SeekOrigin origin )
    {
      throw new NotSupportedException( "This stream does not support seeking." );
    }

    public override void SetLength( long value )
    {
      throw new NotSupportedException( "This stream does not support seeking." );
    }

    public override void Write( byte[] buffer, int offset, int count )
    {
      if( !m_writing )
        throw new IOException( "Cannot write to this stream. It is open for reading." );

      if( buffer == null )
        throw new ArgumentNullException( "buffer" );

      if( ( offset < 0 ) || ( offset >= buffer.Length ) )
        throw new ArgumentOutOfRangeException( "offset" );

      if( count < 0 )
        throw new ArgumentOutOfRangeException( "count" );

      if( offset + count > buffer.Length )
        throw new ArgumentOutOfRangeException( "count" );

      if( count > 0 )
      {
        m_hmac.TransformBlock( buffer, offset, count, buffer, offset );

        m_innerStream.Write( buffer, offset, count );
      }
    }

    protected override void Dispose( bool disposing )
    {
      try
      {
        if( disposing )
        {
          m_hmac.TransformFinalBlock( new byte[ 0 ], 0, 0 );

          byte[] hash = m_hmac.Hash;

          System.Diagnostics.Trace.Assert( hash.Length == ( m_hmac.HashSize / 8 ) );

          if( m_writing )
          {
            System.Diagnostics.Trace.Assert( hash.Length == ( m_hmac.HashSize / 8 ) );

            m_innerStream.Write( hash, 0, hash.Length );
          }
          else
          {
            if( hash.Length != m_bufferAvailable )
              throw new IOException( "Invalid authentication hash length." );

            for( int i = 0; i < hash.Length; i++ )
            {
              if( hash[ i ] != m_buffer[ i ] )
                throw new IOException( "Invalid authentication hash signature." );
            }
          }

          m_innerStream.Close();
          ( ( IDisposable )m_hmac ).Dispose();
        }

        m_innerStream = null;
        m_hmac = null;
      }
      finally
      {
        base.Dispose( disposing );
      }
    }

    private byte[] m_buffer = new byte[ 32768 ];
    private int m_bufferAvailable; // = 0

    private Stream m_innerStream; // = null
    private bool m_writing; // = false
    private HMAC m_hmac; // = null
  }
}

   
    
  








Related examples in the same category

1.Stream.CanRead indicates whether the current stream supports reading.
2.Stream.CanWrite Property indicates whether the current stream supports writing.
3.Stream.CopyTo reads all the bytes from the current stream and writes them to the destination stream.
4.Stream.Read reads a sequence of bytes and advances the position
5.Create StreamWriter class for the specified stream, using the specified encoding and the default buffer size.
6.Create StreamWriter from FileStream
7.Create StreamWriter with encoding
8.Create StreamWriter from file name
9.UTF8 encoding StreamWriter
10.Create UTF8 encoding StreamWriter from File name
11.Set StreamWriter buffer size
12.Writes a subarray of characters to the stream.
13.Create a new instance of the StreamReader class for the specified stream.
14.Gets the current character encoding that the current StreamReader object is using.
15.Returns the next available character but does not consume it.
16.Reads a maximum of count characters from the current stream into buffer, beginning at index.
17.Reads the next character from the input stream and advances the character position by one character.
18.Read a single character
19.Reads a line of characters from the current stream and returns the data as a string.
20.Reads the stream from the current position to the end of the stream.
21.Copy Stream
22.Copy one Stream to another Stream
23.Outputs data from a read stream to a newly created file