FileDataSource.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / infocard / Service / managed / Microsoft / InfoCards / FileDataSource.cs / 2 / FileDataSource.cs

                            //------------------------------------------------------------------------------ 
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
//
// Presharp uses the c# pragma mechanism to supress its warnings. 
// These are not recognised by the base compiler so we need to explictly
// disable the following warnings. See http://winweb/cse/Tools/PREsharp/userguide/default.asp 
// for details. 
//
#pragma warning disable 1634, 1691      // unknown message, unknown pragma 


namespace Microsoft.InfoCards
{ 
    using System;
    using System.Collections; 
    using System.Collections.Generic; 
    using System.ComponentModel;
    using System.IO; 
    using System.Globalization;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Security.AccessControl; 
    using System.Security.Cryptography;
    using System.Security.Principal; 
    using System.Text; 
    using System.Threading;
 
    using IDT = Microsoft.InfoCards.Diagnostics.InfoCardTrace;

    //
    // Summary: 
    //  File base DataSource for storing infocard/ledger information
    // 
    internal class FileDataSource :DataSource 
    {
        public const Int32 ENCRYPTIONKEYBUFFERSIZE = 32; 
        public const Int32 ENCRYPTIONKEYBITLENGTH = ENCRYPTIONKEYBUFFERSIZE * 8;

        public const Int32 ENCRYPTIONBLOCKBUFFERSIZE = 16;
        public const Int32 ENCRYPTIONBLOCKBITLENGTH = ENCRYPTIONBLOCKBUFFERSIZE * 8; 

 
 
        public const Int32 ENCRYPTIONITERATIONS = 1000;
        public const Int32 INITIALINDEXITEMCOUNT = 20; 
        public const Int32 INITIALINDEXAREASIZE = ( INITIALINDEXITEMCOUNT * 4 /*sizeof(Int32)*/ );//room for 25;
        public const Int32 INITIALBLOBAREASIZE = 2048;//
        public const Int32 FILEVERSION = 11;
        public const Int32 FILEVERSIONV2 = 12; 

        private static readonly SecurityIdentifier AdministratorSid 
                                = new SecurityIdentifier( WellKnownSidType.BuiltinAdministratorsSid, null ); 

        // 
        // See SignatureHeader.IVSize OR m_encAlg.IV.Length for IV Size
        //

        // 
        // This is the signature length for the current version of the store.
        // 
        int m_variableSignatureLength = 64; 

        FileInfo m_fileInfo; 
        FileStream m_file;
        FileInfo m_shadowInfo;
        FileStream m_shadow;
        byte[ ] m_header; 
        IndexedDataBuffer m_data;
        SecondaryIndexList m_indexes; 
        ReaderWriterLock m_lock; 
        SymmetricAlgorithm m_encAlg;
        byte[ ] m_key; 
        bool m_keyProtected;
        WindowsIdentity m_identity;
        bool m_currentTransactionDirty;
        SafeRsaProviderHandle m_provider; 
        byte[ ] m_encryptedKey;
 
 

 
        //
        // Summary:
        //  Protected default ctor for tools to use.
        // 
        protected FileDataSource()
            : base( null, null ) 
        { 
        }
 
        //
        // Summary:
        //  Creates an instance of a FileDataSource to store and index data object
        // 
        // Remarks:
        //  if fileName does not exist, it will be created. 
        //  will be created using SecondaryIndexDefinition.MasterIndexes as the index mapping 
        //
        // Parameters: 
        //  identity:           The WindowsIdentity to use to when decrypting the keys use DPAPI
        //  fileName:           The full path to the file to open.
        //
        public FileDataSource( WindowsIdentity identity, string fileName ) 
            : this( identity, fileName, Guid.NewGuid().ToString( "P" ), SecondaryIndexDefinition.MasterIndexes )
        { 
        } 

        // 
        // Summary:
        //  Creates an instance of a FileDataSource to store and index data object
        //
        // Remarks: 
        //  if fileName does not exist, it will be created.
        // 
        // Parameters: 
        //  identity:           The WindowsIdentity to use to when decrypting the keys use DPAPI
        //  fileName:           The full path to the file to open. 
        //  instanceId:         The id of the instance that owns this object.
        //  indexDefinitions:   The list of index definitions to map over this file.
        //
        public FileDataSource( 
            WindowsIdentity identity,
            string fileName, 
            string instanceId, 
            SecondaryIndexDefinition[ ] indexDefinitions )
            : base( instanceId, fileName ) 
        {
            m_identity = identity;
            m_currentTransactionDirty = false;
 
            m_fileInfo = new FileInfo( fileName );
            m_shadowInfo = new FileInfo( m_fileInfo.FullName + ".shadow" ); 
 
            m_lock = new ReaderWriterLock();
 
            m_indexes = new SecondaryIndexList( indexDefinitions );
            m_encAlg = CreateEncryptionAlg();

            // 
            // Setup the native provider who will do the encryption and decryption for us.
            // 
            SetupProvider(); 

        } 

        //
        // Summary:
        //  Gets the internal buffer that contains all of the data blobs. 
        //
        // Remarks: 
        //  This is only exposed to assist tools and debugging. 
        //
        public IndexedDataBuffer Buffer 
        {
            get
            {
                ThrowIfDisposed(); 
                ThrowIfNotLoaded();
                return m_data; 
            } 
        }
 
        //
        // Summary:
        //  Gets the internal buffer that file header information.
        // 
        // Remarks:
        //  This is only exposed to assist tools and debugging. 
        // 
        public byte[ ] Header
        { 
            get
            {
                ThrowIfDisposed();
                ThrowIfNotLoaded(); 
                return m_header;
            } 
        } 

        // 
        // Summary:
        //  Gets the Collection of SecondaryIndexes that can be used for searching.
        //
        // Remarks: 
        //  These index structures are specific to the file implementation, there for
        //      they are not defined in the base class. 
        //  This is only exposed to assist tools and debugging. 
        //
        public SecondaryIndexList Indexes 
        {
            get
            {
                ThrowIfDisposed(); 
                ThrowIfNotLoaded();
                return m_indexes; 
            } 
        }
 
        //
        // Summary:
        //  Identity of the owner of the data source.
        // 
        protected WindowsIdentity Identity
        { 
            get { return m_identity; } 
        }
 
        //
        // Summary:
        //  Override for handling the Delete events
        // 
        protected internal override void OnClear()
        { 
            using( SystemIdentity lsa = new SystemIdentity( true ) ) 
            {
 
                ResilientDelete( m_fileInfo );
                ResilientDelete( m_shadowInfo );
            }
        } 

 
 
        //
        // Summary: 
        //  Override for handling the Load events
        //
        protected internal override void OnLoad()
        { 
            ThrowIfDisposed();
 
            IDT.TraceDebug( "STORE: Opening file with READ/WRITE:{0} ", m_fileInfo.FullName ); 
            IDT.TraceDebug( "STORE: Opening shadow file with READ/WRITE:{0} ", m_shadowInfo.FullName );
 
            try
            {
                //
                // Create file required for the store 
                //
                CreateDirAndFiles(); 
 
                if( m_shadow.Length > 0 )
                { 
                    //
                    // We had a failed write, or the store didn't close properly
                    //
                    if( m_file.Length > 0 ) 
                    {
                        // 
                        // RollBack: 
                        // Original file seem intact, we will load it.
                        // 
                        m_shadow.SetLength( 0 );
                        LoadFrom( m_file );
                    }
                    else 
                    {
                        // 
                        // RollForward: 
                        // Original file seem corrupted, rewrite
                        // 
                        LoadFrom( m_shadow );
                        FlushToShadow();
                        SwapFileWithShadow();
                    } 
                }
                else 
                { 
                    //
                    // No shadow file. 
                    //
                    if( m_file.Length > 0 )
                    {
                        // 
                        // All OK:
                        // Original file seems ok, 
                        // 
                        LoadFrom( m_file );
                    } 
                    else
                    {
                        //
                        // New Store: 
                        //  No storage files are present
                        // 
                        CreateEmptyStore(); 
                        FlushToShadow();
                        SwapFileWithShadow(); 
                    }
                }

                m_file.Seek( 0, SeekOrigin.Begin ); 
            }
            catch 
            { 
                CloseFiles();
                throw; 
            }
        }

        void CloseFiles() 
        {
            using( SystemIdentity lsa = new SystemIdentity( false ) ) 
            { 
                if( null != m_file )
                { 
                    m_file.Close();
                    m_file = null;
                }
                if( null != m_shadow ) 
                {
                    m_shadow.Close(); 
                    m_shadow = null; 
                }
            } 
        }

        //
        // Virutal functions that can be overriden 
        //
 
        // 
        // Summary:
        //    Helper mehtod to create directory if it does not exist 
        //    and to open/create the file with read/write access
        //
        protected void CreateDirAndFiles()
        { 

            // 
            // Validate that the user has the appropriate 
            // access to the infocard directory. This can throw DataAccessExceptions .
            // 
            CheckReparsePoints();

            if( NativeMcppMethods.PathSupportsPersistedSecurity( m_fileInfo.Directory.FullName ) )
            { 
                ValidateDirectoryAccess();
            } 
 
            using( SystemIdentity lsa = new SystemIdentity( true ) )
            { 
                try
                {

 
                    //
                    // Create directory if necessary 
                    // 
                    if( !m_fileInfo.Directory.Exists )
                    { 
                        //
                        // Create directory: Scenario - user starting infocard for the first time
                        //
                        try 
                        {
                            if( NativeMcppMethods.PathSupportsPersistedSecurity( m_fileInfo.Directory.FullName ) ) 
                            { 
                                m_fileInfo.Directory.Create( CreateSecurityDescriptor() );
                            } 
                            else
                            {
                                m_fileInfo.Directory.Create();
                            } 
                            m_fileInfo.Directory.Attributes |= FileAttributes.NotContentIndexed | FileAttributes.Hidden;
 
                        } 
                        catch( UnauthorizedAccessException uae )
                        { 
                            //
                            // We do not even have permissions to create the directory
                            //
                            throw IDT.ThrowHelperError( new DataAccessException( uae.Message ) ); 
                        }
                    } 
                    else if(    NativeMcppMethods.PathSupportsPersistedSecurity( m_fileInfo.Directory.FullName ) && 
                                LogIfAclsTampered( m_fileInfo.Directory ) )
                    { 
                        //
                        // Directory exists but acls modified
                        //
                        m_fileInfo.Directory.SetAccessControl( CreateSecurityDescriptor() ); 
                    }
 
                    IDT.Assert( m_shadowInfo.Directory.Exists, "m_shadowInfo directory should be same as main directory" ); 

                    // 
                    // Open or create the main file
                    //
                    OpenOrCreateHelper( m_fileInfo, out m_file );
 
                    //
                    // Open or create the shadow file 
                    // 
                    OpenOrCreateHelper( m_shadowInfo, out m_shadow );
                } 
                catch( IOException ioe )
                {
                    //
                    // Race conditions could lead to IOExceptions for example 
                    // where a file is created externally just after we checked it does not exist.
                    // We should not crash then so translate to a non-fatal exception. 
                    // 
                    throw IDT.ThrowHelperError( new DataAccessException( ioe.Message ) );
                } 
            }
        }

 
        //
        // Summary: 
        // Validates that no directory in infocard path contains reparse points. 
        //
        private void CheckReparsePoints() 
        {
            using( SystemIdentity lsa = new SystemIdentity( true ) )
            {
                DirectoryInfo f = m_fileInfo.Directory; 
                while( null != f )
                { 
                    if( f.Exists ) 
                    {
                        if( ( f.Attributes & FileAttributes.ReparsePoint ) != 0 ) 
                        {
                            throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreNoReparsePointAllowed ) ) );
                        }
                    } 
                    f = f.Parent;
                } 
 
                //
                // Check file for reparse points. use directory check as reparse points are created as directories 
                //
                if( Directory.Exists( m_fileInfo.FullName ) )
                {
                    if( ( m_fileInfo.Attributes & FileAttributes.ReparsePoint ) != 0 ) 
                    {
                        throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreNoReparsePointAllowed ) ) ); 
                    } 
                }
            } 


        }
 
        //
        // Summary: 
        // Validates that m_fileInfo has the appropriate access levels. 
        //
        private void ValidateDirectoryAccess() 
        {
            IDT.Assert( !WindowsIdentity.GetCurrent().IsSystem, "Should not be running as system" );

            IdentityReference user = WindowsIdentity.GetCurrent().User.Translate( typeof( NTAccount ) ); 

            bool allowed = false; 
 
            //
            // The parent directory must exist. 
            //
            DirectoryInfo parent = m_fileInfo.Directory.Parent;
            if( parent.Exists )
            { 
                //
                // The current user must have full control. Include both inherited and explicit rights. 
                // 
                AuthorizationRuleCollection rules = parent.GetAccessControl().GetAccessRules(
                                                                                        true, 
                                                                                        true,
                                                                                        typeof( NTAccount ) );
                foreach( FileSystemAccessRule rule in rules )
                { 
                    if( rule.IdentityReference == user
                        && rule.FileSystemRights == FileSystemRights.FullControl 
                        && rule.AccessControlType == AccessControlType.Allow ) 
                    {
                        allowed = true; 
                        break;

                    }
 
                }
            } 
            if( !allowed ) 
            {
                throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreInvalidDataFilePath ) ) ); 
            }
            return;

        } 

        // 
        // Summary: 
        // Open or create file pointed to by the fileInfo object and init
        // the fileStream 
        //
        // Params:
        // theFile - the file we want to open or create
        // fileStream - the r/w stream that needs to be initialized 
        //
        private void OpenOrCreateHelper( FileInfo theFile, out FileStream fileStream ) 
        { 
            IDT.Assert( WindowsIdentity.GetCurrent().IsSystem, "Should be running as system" );
 
            //
            // File.Exists works more reliably than theFile.Exists
            //
            if( !File.Exists( theFile.FullName ) ) 
            {
                // 
                // We just ACLed the directory, so we should be able to create... 
                //
                fileStream = ResilientOpen( theFile, FileMode.CreateNew, FileAccess.ReadWrite ); 


                theFile.Attributes |= FileAttributes.NotContentIndexed | FileAttributes.Hidden;
                theFile.SetAccessControl( CreateSecurityDescriptor() ); 
            }
            else 
            { 
                if( NativeMcppMethods.PathSupportsPersistedSecurity( m_fileInfo.Directory.FullName ) &&
                    LogIfAclsTampered( theFile ) ) 
                {
                    //
                    // Reset the ACL since we may not have permission to open the file
                    // 
                    theFile.SetAccessControl( CreateSecurityDescriptor() );
                    theFile.Attributes |= FileAttributes.NotContentIndexed | FileAttributes.Hidden; 
                } 

                // 
                // Now open the file
                //
                fileStream = ResilientOpen( theFile, FileMode.Open, FileAccess.ReadWrite );
 

            } 
        } 

        // 
        // Summary:
        //   Implements the logic to delete main file,
        //   move shadow file to main file, and to create
        //   an empty shadow file 
        //
        protected virtual void OnSwapFileWithShadow() 
        { 
            using( SystemIdentity lsa = new SystemIdentity( true ) )
            { 
                string fileName = m_fileInfo.FullName;
                ResilientDelete( m_fileInfo );
                ResilientMove( m_shadowInfo, m_fileInfo );
                m_fileInfo = new FileInfo( fileName ); 
                m_shadowInfo = new FileInfo( fileName + ".shadow" );
            } 
        } 

        // 
        // Summary:
        //  Override for handling the Close events
        //
        protected internal override void OnClose() 
        {
            ThrowIfDisposed(); 
 
            //
            // m_data is null in event of 0 file size 
            //
            if( null != m_data )
            {
                m_data.Close(); 
                m_data = null;
            } 
            m_indexes.Close(); 

            CloseFiles(); 

            m_provider.Dispose();

        } 

        // 
        // Summary: 
        //  Definition for Reading a Row from the data source, using a local identifier.
        // 
        // Remarks:
        //  if QueryDetals.Identifiers or QueryDetails.None is specified, NULL wil be returned.
        //
        // Parameters: 
        //  localId:    The local identifier of the object to read.
        //  details:    The level of detail to return from the query. 
        // 
        // Returns:
        //  The data row representing object. 
        //
        protected internal override DataRow ReadRow( Int32 localId, QueryDetails details )
        {
            DataRow row = null; 
            if( QueryDetails.Header == ( details & QueryDetails.Header ) )
            { 
                // 
                // Create a data row, including all header info
                // 
                row = m_data.CreateDataRow( localId );

                //
                // Read and decrypt the blob if required. 
                //
                if( QueryDetails.DataBlob == ( details & QueryDetails.DataBlob ) ) 
                { 
                    byte[ ] iv = AllocateIvBuffer();
                    m_data.CopyIVFromObject( localId, iv, 0 ); 

                    using( Stream encrypted = m_data.GetStreamForObjectData( localId ) )
                    {
                        using( MemoryStream decryptedStream = new MemoryStream( (int)encrypted.Length ) ) 
                        {
                            DecryptData( iv, encrypted, decryptedStream ); 
 
                            //
                            // This will re-allocate a new array that is the correct size. 
                            //
                            row.SetDataField( decryptedStream.ToArray() );

                            // 
                            // Becuase we allocated a new array above, we left the
                            //  sensitive data in the other array. We will will clear 
                            //  the other array manually to ensure no data disclosure. 
                            //
                            byte[ ] leftOver = decryptedStream.GetBuffer(); 
                            Array.Clear( leftOver, 0, leftOver.Length );
                        }
                    }
                } 

                // 
                // If we are supposed to get all index data, scrape all secondary indexes 
                //  for any value that pertains to this object.
                // 
                if( QueryDetails.IndexData == ( details & QueryDetails.IndexData ) )
                {
                    m_indexes.PopulateRowIndexBuffer( row );
                } 
            }
            return row; 
        } 

        // 
        // Summary:
        //  Performs a Single match using the specified query data.
        //
        // Remarks: 
        //
        // Parameters: 
        //  match:      The QueryParameter you want to match against. 
        //  localIds:   The list of LocalId values that have matched
        //              in previous queries, all results will will be 
        //              anded with this list.
        // Returns:
        //  boolean indicating a successful query match
        // 
        protected internal override bool SingleMatch( QueryParameter match, LocalIdCollection localIds )
        { 
            ThrowIfDisposed(); 
            ThrowIfNotLoaded();
 
            if( null == localIds )
            {
                throw IDT.ThrowHelperArgumentNull( "localIds" );
            } 
            if( null == match )
            { 
                throw IDT.ThrowHelperArgumentNull( "match" ); 
            }
            if( String.IsNullOrEmpty( match.IndexName ) ) 
            {
                throw IDT.ThrowHelperError( new ArgumentException(
                                SR.GetString( SR.StoreDataSourceInvalidIndexName, match.IndexName ),
                                "match" ) ); 
            }
 
            bool lockHeld = false; 
            try
            { 
                try { }
                finally
                {
                    m_lock.AcquireReaderLock( 0 ); 
                    lockHeld = true;
                } 
                return m_indexes.Match( match, localIds ); 
            }
            finally 
            {
                if( lockHeld )
                {
                    m_lock.ReleaseReaderLock(); 
                }
            } 
        } 

        // 
        // Summary:
        //  Handle BeginTransaction Event.
        //
        protected internal override void OnBeginTransaction() 
        {
            IDT.Assert( false == m_currentTransactionDirty, 
                    "m_currentTransactionDirty should have been set to false before beginning a transaction" ); 
            base.OnBeginTransaction();
        } 

        //
        // Summary:
        //  Handle Rollback Event 
        //
        // Remarks: 
        //  On Rollbacks, we simply re-load the data buffers from 
        //  the original data file.
        // 
        protected internal override void OnRollbackTransaction()
        {
            try
            { 
                base.OnRollbackTransaction();
                LoadFrom( m_file ); 
            } 
            finally
            { 
                m_currentTransactionDirty = false;
            }
        }
 
        //
        // Summary: 
        //  Handle the Commit Event 
        //
        // Remarks: 
        //  On Commit, we flush the data to the shadow file,
        //  then swap this main file with the shadow file.
        //
        protected internal override void OnCommitTransaction() 
        {
            try 
            { 
                base.OnCommitTransaction();
                if( m_currentTransactionDirty ) 
                {
                    FlushToShadow();
                    SwapFileWithShadow();
                } 
            }
            finally 
            { 
                m_currentTransactionDirty = false;
            } 
        }

        //
        // Summary: 
        //  Writes a DataRow into the data buffers, using the
        //  index and identifier information with in the datarow. 
        // 
        // Remarks:
        // 
        //
        // Parameters:
        //  row:        The row to write to the instance.
        // 
        protected internal override void WriteRow( DataRow row )
        { 
            ThrowIfDisposed(); 
            ThrowIfNotLoaded();
            ThrowIfWriteLockNotHeld(); 

            if( null == row )
            {
                throw IDT.ThrowHelperArgumentNull( "row" ); 
            }
 
            bool lockHeld = false; 
            try
            { 
                try { }
                finally
                {
                    m_lock.AcquireWriterLock( 0 ); 
                    lockHeld = true;
                } 
 
                byte[ ] iv;
                byte[ ] dataField; 
                Int32 id;
                using( MemoryStream encrypted = new MemoryStream() )
                {
                    m_encAlg.GenerateIV(); 
                    iv = m_encAlg.IV;
                    dataField = row.GetDataField(); 
                    using( MemoryStream dataFieldStream = new MemoryStream( dataField ) ) 
                    {
                        EncryptData( iv, dataFieldStream, encrypted ); 

                        id = m_data.WriteObject(
                                            row.LocalId,
                                            iv, 
                                            encrypted.ToArray(),//this re-allocs, maybe there is a better way?
                                            row.ObjectType, 
                                            row.LastChange, 
                                            row.GlobalId );
 
                        m_indexes.SetValuesForId( id, row.IndexBuffer, true );

                        row.SourceId = SourceId;
                        row.InstanceId = InstanceId; 
                        row.LocalId = id;
                    } 
                } 
            }
            finally 
            {
                if( lockHeld )
                {
                    m_lock.ReleaseWriterLock(); 
                }
            } 
 
            //
            // We wrote to store in memory, so set the dirty flag 
            //
            m_currentTransactionDirty = true;
        }
 
        //
        // Summary: 
        //  Removes a DataObject from this data source. 
        //
        // Remarks: 
        //
        // Parameters:
        //  id:         The LocalId of the data object to remove.
        // 
        protected internal unsafe override void RemoveObject( Int32 id )
        { 
            if( (UInt32)id > (UInt32)( ( m_data.Index.Length / sizeof( Int32 ) ) - 1 ) ) 
            {
                throw IDT.ThrowHelperError( new ArgumentOutOfRangeException( 
                                    "id",
                                    id,
                                    SR.GetString( SR.StoreDataSourceIdOutOfRange ) ) );
            } 

            bool lockHeld = false; 
            try 
            {
                try { } 
                finally
                {
                    m_lock.AcquireWriterLock( 0 );
                    lockHeld = true; 
                }
                m_data.RemoveObject( id ); 
                m_indexes.RemoveAllValuesForId( id ); 
            }
            finally 
            {
                if( lockHeld )
                {
                    m_lock.ReleaseWriterLock(); 
                }
            } 
 
            //
            // We wrote to store in memory, so set the dirty flag 
            //
            m_currentTransactionDirty = true;
        }
 
        //
        // Summary: 
        //  Encrypts the m_key member using DPAPI. 
        //
        unsafe void ProtectKey() 
        {
            if( m_keyProtected )
            {
                throw IDT.ThrowHelperError( new InvalidOperationException( 
                                SR.GetString( SR.StoreKeyAlreadyProtected ) ) );
            } 
            ProtectedMemory.Protect( m_key, MemoryProtectionScope.SameProcess ); 

            m_keyProtected = true; 
        }

        //
        // Summary: 
        //  Decrypts the m_key member using DPAPI.
        // 
        unsafe void UnprotectKey() 
        {
            if( !m_keyProtected ) 
            {
                throw IDT.ThrowHelperError( new InvalidOperationException(
                                SR.GetString( SR.StoreKeyNotAlreadyProtected ) ) );
            } 

            ProtectedMemory.Unprotect( m_key, MemoryProtectionScope.SameProcess ); 
 
            m_keyProtected = false;
        } 

        //
        // Summary:
        //  Decrypts data from one stream into another. 
        //
        // Remarks: 
        //  Data in the input stream should be clear and disposed when finished. 
        //
        // Parameters: 
        //  iv:         The initialization vector to use for encryption
        //  input:      The input stream to encrypt
        //  output:     The stream to hold the encrypted data.
        // 
        unsafe void DecryptData( byte[ ] iv, Stream input, Stream output )
        { 
            Exception e = null; 
            IDT.Assert( input is MemoryStream, "Invalid stream type" );
 
            if( null == iv || iv.Length == 0 )
            {
                throw IDT.ThrowHelperArgumentNull( "iv" );
            } 
            if( null == input )
            { 
                throw IDT.ThrowHelperArgumentNull( "input" ); 
            }
            if( null == output ) 
            {
                throw IDT.ThrowHelperArgumentNull( "output" );
            }
 
            using( SafeCryptoKey key = new SafeCryptoKey( m_provider, m_key, iv ) )
            { 
 
                byte[ ] buffer = new byte[ input.Length ];
                input.Seek( 0, SeekOrigin.Begin ); 
                input.Read( buffer, 0, (int)input.Length );
                uint length = (uint)buffer.Length;

                // 
                // Encrypt the data returning the length + padding.
                // 
                fixed( byte* pB = &buffer[ 0 ] ) 
                {
                    if( !NativeMethods.CryptDecrypt( 
                            key.Handle,
                            IntPtr.Zero,
                            1,
                            0, 
                            (IntPtr)pB,
                            ref length ) ) 
                    { 
                        e = new Win32Exception( Marshal.GetLastWin32Error() );
                        Array.Clear( buffer, 0, buffer.Length ); 
                        IDT.TraceAndLogException( e );
                        throw IDT.ThrowHelperError( e );
                    }
                } 

                output.Write( buffer, 0, (int)length ); 
            } 
        }
 
        //
        // Summary:
        //  Encrypts data from one stream into another.
        // 
        // Remarks:
        //  Data in the input stream should be clear and disposed when finished. 
        // 
        // Parameters:
        //  iv:         The initialization vector to use for encryption 
        //  input:      The input stream to encrypt
        //  output:     The stream to hold the encrypted data.
        //
        unsafe void EncryptData( byte[ ] iv, Stream input, Stream output ) 
        {
            Exception e = null; 
            if( null == iv || iv.Length == 0 ) 
            {
                throw IDT.ThrowHelperArgumentNull( "iv" ); 
            }
            if( null == input )
            {
                throw IDT.ThrowHelperArgumentNull( "input" ); 
            }
            if( null == output ) 
            { 
                throw IDT.ThrowHelperArgumentNull( "output" );
            } 

            using( SafeCryptoKey key = new SafeCryptoKey( m_provider, m_key, iv ) )
            {
                // 
                // Encrypting could require at least a block length's padding, so add it
                // as appropriate. 
                // 
                byte[ ] buffer = new byte[ input.Length + m_key.Length ];
                input.Seek( 0, SeekOrigin.Begin ); 
                input.Read( buffer, 0, (int)input.Length );

                uint realLength = (uint)input.Length;
 
                //
                // Encrypt the data returning the length + padding. 
                // 
                fixed( byte* pB = &buffer[ 0 ] )
                { 
                    if( !NativeMethods.CryptEncrypt(
                            key.Handle,
                            IntPtr.Zero,
                            1, 
                            0,
                            (IntPtr)pB, 
                            ref realLength, 
                            (uint)buffer.Length ) )
                    { 
                        e = new Win32Exception( Marshal.GetLastWin32Error() );
                        IDT.TraceAndLogException( e );
                        throw IDT.ThrowHelperError( e );
                    } 
                }
 
                output.Write( buffer, 0, (int)realLength ); 
            }
        } 

        //
        // Summary:
        //  Creates an default store buffers 
        //
        unsafe void CreateEmptyStore() 
        { 
            //
            // Allocate a masterIndex and Data buffer. 
            //
            byte[ ] mIndex = new byte[ INITIALINDEXAREASIZE ];
            byte[ ] data = new byte[ INITIALBLOBAREASIZE ];
 
            //
            // Create the IndexedDataBuffer from mIndex and data 
            // 
            m_data = new IndexedDataBuffer( mIndex, data, 0, this );
 
            //
            // Create and protect the key
            //
            m_encAlg.GenerateKey(); 
            m_key = new byte[ m_encAlg.Key.Length ];
            Array.Copy( 
                m_encAlg.Key, 
                0,
                m_key, 
                0,
                m_encAlg.Key.Length
            );
 
            //
            // It is invalid to clear the key, 
            // and the SymmetricAlgorithm.Clear method will dispose the object 
            // which we don't want.  So, we will generate a new key,
            // thus erasing the key that was there. 
            //
            m_encAlg.GenerateKey();

            // 
            // Protect our copy of the key.
            // 
            m_keyProtected = false; 
            ProtectKey();
 
            //
            // Allocate the File Header buffer.
            //
            m_header = new byte[ GetTotalHeaderSize() ]; 

            // 
            // Set the version in the header 
            //
            fixed( byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) 
            {
                EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer;
                //
                // From now on we write the new version of the header 
                //
                pHeader->Version = FILEVERSIONV2; 
            } 

            // 
            // Encrypt and protect the store key
            // into the m_header buffer.
            //
            EncryptAndSaveDPAPIKeyToHeader(); 
        }
 
        // 
        // Summary:
        //  Flushes all data buffers to the shadow file 
        //
        void FlushToShadow()
        {
            FlushToStream( m_shadow ); 
        }
 
        // 
        // Summary:
        //  Gets the size of header including the signature header 
        //
        unsafe int GetTotalHeaderSize()
        {
            return sizeof( EncryptedFileStoreHeader ) + GetTotalSignatureSize(); 
        }
 
        // 
        // Summary:
        // Gets the offset to the EncryptedFileHeader which is located after the signature header 
        //
        int GetOffsetToEncryptedFileHeader()
        {
            return m_variableSignatureLength + sizeof( Int32 ); 
        }
 
        // 
        // Summary:
        // Gets the size of the signature header 
        //
        int GetTotalSignatureSize()
        {
            return GetOffsetToEncryptedFileHeader(); 
        }
 
        // 
        // Helper function used to calculate how much space is to be allocated for an output
        // file data source 
        //
        unsafe Int64 GetRequiredFileSize()
        {
            //length of header 
            Int64 size = Convert.ToInt64( GetTotalHeaderSize() );
 
            // 
            // Add the length of the encryptedKey
            // 
            size += Convert.ToInt64( m_encryptedKey.Length );

            //
            // Length of data 
            //
            size += Convert.ToInt64( m_data.DataLength ); 
 
            //
            // Length of master index. 
            //
            size += Convert.ToInt64( m_data.Index.Length + sizeof( Int32 ) );

 
            foreach( string key in m_indexes.InnerIndexes.Keys )
            { 
                byte[ ] indexBuffer = ( (SecondaryIndex)m_indexes.InnerIndexes[ key ] ).GetBuffer(); 
                size += Convert.ToInt64( sizeof( Int32 ) + sizeof( Int32 ) + indexBuffer.Length );
            } 

            //
            // Give 5% buffer room.
            // 
            size += size / 20;
            return size; 
        } 

        // 
        // Summary:
        //  Flushes all data buffer to the specified stream.
        //
        // Remarks: 
        //
        // Parameters: 
        //  stream:         the data stream to write to 
        //
        void FlushToStream( Stream stream ) 
        {
            if( null == stream )
            {
                throw IDT.ThrowHelperArgumentNull( "stream" ); 
            }
 
            stream.SetLength( GetRequiredFileSize() ); 

            const UInt32 version = FILEVERSIONV2; 
            Int32 dataSize = 0;
            Int32 fullSize = 0;
            Int32 indexSize = 0;
            byte[ ] iv = null; 
            BinaryWriter writer = new BinaryWriter( stream );
            Int64 indexStart = 0; 
 
            //
            // Create a new IV for the indexes 
            //
            m_encAlg.GenerateIV();
            iv = m_encAlg.IV;
 
            //
            // Capture the data size. 
            // 
            dataSize = m_data.DataLength;
            unsafe 
            {
                //
                // Skip past the header for now, we will come back to it
                // after we gather up all the require information. 
                // the data we are working is can be large, so we don't
                // want to have to create temp working space to calculate 
                // sizes.  We will just do it as we require. 
                //
                stream.Seek( GetTotalHeaderSize(), SeekOrigin.Begin ); 

                //
                // Write the key and the stream automatically advances
                // 
                writer.Write( m_encryptedKey );
 
                // 
                // Write the IV
                // 
                writer.Write( iv );

                //
                // Capture the location in the stream 
                // where the index starts
                // 
                indexStart = stream.Position; 

                // 
                // Create a temp MemoryStream to hold the decrypted index buffers
                //
                using( BinaryWriter indexStream = new BinaryWriter( new MemoryStream() ) )
                { 
                    //
                    // Write the count of MasterIndex items and then the buffer 
                    // 
                    indexStream.Write( m_data.Index.Length / sizeof( Int32 ) );
                    indexStream.Write( m_data.Index ); 

                    //
                    // Write the count of secondary indexes.
                    // 
                    indexStream.Write( m_indexes.Count );
 
                    foreach( string key in m_indexes.InnerIndexes.Keys ) 
                    {
                        // 
                        // Write the count of SecondaryIndexItem entries in the buffer,
                        // the lastIndex value, and the buffer for the index.
                        //
 
                        SecondaryIndex secIndex = (SecondaryIndex)m_indexes.InnerIndexes[ key ];
                        int lastIndex = secIndex.LastIndex; 
 
                        indexStream.Write( lastIndex );
 
                        int minCapacity = SecondaryIndexDefinition.GetByName( key ).InitialSize;

                        if( lastIndex + 1 < minCapacity )
                        { 
                            //
                            // Write the minimum capacity of elements 
                            // 
                            indexStream.Write( minCapacity );
 
                            IDT.Assert( ( minCapacity * sizeof( SecondaryIndexItem ) ) <=
                                secIndex.GetBuffer().Length,
                                "Buffer length is at least min capacity" );
 
                            indexStream.Write( secIndex.GetBuffer(), 0, minCapacity * sizeof( SecondaryIndexItem ) );
                        } 
                        else 
                        {
                            // 
                            // Write only last index amount of entries, i.e. no unused SecondaryIndexItems are persisted
                            //
                            indexStream.Write( lastIndex + 1 );
                            indexStream.Write( secIndex.GetBuffer(), 0, ( lastIndex + 1 ) * sizeof( SecondaryIndexItem ) ); 
                        }
                    } 
                    indexStream.Flush(); 

                    // 
                    // Seek the temp stream back to the beginning.
                    //

                    indexStream.Seek( 0, SeekOrigin.Begin ); 
                    //
                    // Encrypt the temp memory stream directly to the 
                    // incoming stream param. 
                    //
                    EncryptData( 
                            iv,
                            indexStream.BaseStream,
                            writer.BaseStream );
                } 

                // 
                // Capture the size of the index based off the new position 
                // of the stream vs the position we captured before we started.
                // 
                indexSize = Convert.ToInt32( stream.Position - indexStart );

                //
                // Capture the full size of the file. 
                //
                fullSize = indexSize + m_data.DataLength + GetTotalHeaderSize() + iv.Length + m_encryptedKey.Length; 
 
                //
                // Write out the data length. 
                //
                writer.Write( m_data.Data, 0, m_data.DataLength );

                // 
                // Seek back to the start, and write the header.
                // 
                stream.Seek( 0, SeekOrigin.Begin ); 

                // 
                // Flush the data to the steram, so we can write
                // the header information safely.
                //
                writer.Flush(); 

                // 
                // Syncronize the header buffer with the other objects. 
                //
                fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) 
                {
                    EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer;

                    pHeader->DataSize = dataSize; 
                    pHeader->Version = version;
                    pHeader->Size = fullSize; 
                    pHeader->IndexSize = indexSize; 
                }
            } 

            //
            // Write the header buffer
            // 
            writer.Write( m_header );
 
            // 
            // Set the length to the caluclated size.
            // 
            stream.SetLength( Convert.ToInt64( fullSize ) );

            //
            // Flush the data back to the disk. 
            //
            writer.Flush(); 
 
            //
            // Sign the stream using the key. 
            //
            SignStream( stream );
        }
 
        //
        // Summary: 
        //  Reads and signs a stream. 
        //
        // Remarks: 
        //  Signature is written to first block of the file.
        //  Only the non-signature data is signed.
        //
        // Parameters: 
        //  input:          input stream to read and sign
        // 
        void SignStream( Stream input ) 
        {
            if( null == input ) 
            {
                throw IDT.ThrowHelperArgumentNull( "input" );
            }
 
            long pos = input.Position;
 
            m_encAlg.GenerateIV(); 
            byte[ ] iv = m_encAlg.IV;
 
            byte[ ] signature = SignDigest(
                                    iv,
                                    CreateStreamDigest( input ) );
 

 
            // 
            // Allocate the Signature Header buffer.
            // 
            byte[ ] m_signatureHeader = new byte[ GetTotalSignatureSize() ];
            unsafe
            {
                fixed( byte* pSignatureHeaderBuffer = &m_signatureHeader[ 0 ] ) 
                {
                    SignatureHeader* pSignatureHeader = (SignatureHeader*)pSignatureHeaderBuffer; 
                    pSignatureHeader->SignatureSize = m_variableSignatureLength; 

                } 
            }
            Array.Copy( signature, 0, m_signatureHeader, sizeof( Int32 ), signature.Length );

            // 
            // Write in the signature header at the start of the stream
            // 
            input.Seek( 0, SeekOrigin.Begin ); 
            input.Write( m_signatureHeader, 0, m_signatureHeader.Length );
 
            IDT.Assert( m_variableSignatureLength >= signature.Length, "Generated signature should be less than signature length" );

            input.Seek( pos, SeekOrigin.Begin );
        } 

        byte[ ] CreateStreamDigest( Stream input ) 
        { 
            input.Seek(
                    GetOffsetToEncryptedFileHeader(), 
                    SeekOrigin.Begin );

            //
            // Enough space to hold all 4096 segment hashes plus any remaining. 
            //
            Int32 totalSpace = Convert.ToInt32( ( ( input.Length / 4096 ) + 1 ) * HashUtility.HashBufferLength ); 
 
            byte[ ] digest = new byte[ totalSpace ];
 
            int offset = 0;
            byte[ ] tmp = new byte[ 4096 ];
            int bytesRead = 0;
            do 
            {
                bytesRead = input.Read( tmp, 0, tmp.Length ); 
                if( bytesRead > 0 ) 
                {
                    HashUtility.SetHashValue( 
                                    digest,
                                    offset,
                                    tmp,
                                    0, 
                                    bytesRead );
 
                    offset += HashUtility.HashBufferLength; 
                }
            } while( bytesRead == tmp.Length ); 
            return digest;
        }
        byte[ ] SignDigest( byte[ ] iv, byte[ ] digest )
        { 
            byte[ ] output = new byte[ m_variableSignatureLength ];
 
            using( MemoryStream input = new MemoryStream( digest ) ) 
            {
                using( MemoryStream encrypted = new MemoryStream( digest.Length ) ) 
                {
                    EncryptData( iv, input, encrypted );

                    byte[ ] fullData = encrypted.GetBuffer(); 
                    int start = Convert.ToInt32( encrypted.Position - 1 ) - iv.Length;
                    Array.Copy( fullData, start, output, 0, iv.Length ); 
                    Array.Copy( iv, 0, output, iv.Length, iv.Length ); 
                    return output;
                } 
            }

        }
 
        //
        // Summary: 
        //  Validates the signature of the provided stream. 
        //
        // Remarks: 
        //  throws InvalidOperationException when the signature is invalid.
        //
        // Parameters:
        //  input:  The stream who signature needs validated. 
        //
        void ValidateSignature( Stream input ) 
        { 
            if( null == input )
            { 
                throw IDT.ThrowHelperArgumentNull( "input" );
            }

            long pos = input.Position; 

            byte[ ] sigToMatch = new byte[ m_variableSignatureLength ]; 
            byte[ ] iv = AllocateIvBuffer(); 
            byte[ ] signature;
 
            //
            // Read the IV
            //
            input.Seek( SignatureHeader.IVOffset, SeekOrigin.Begin ); 
            input.Read( iv, 0, iv.Length );
 
            // 
            // Generate the signature.
            // 
            signature = SignDigest(
                                    iv,
                                    CreateStreamDigest( input ) );
 
            //
            // Read the existing signature. 
            // 
            input.Seek( SignatureHeader.SignatureOffset, SeekOrigin.Begin );
            input.Read( sigToMatch, 0, sigToMatch.Length ); 

            for( int i = 0; i < sigToMatch.Length; i++ )
            {
                if( sigToMatch[ i ] != signature[ i ] ) 
                {
                    throw IDT.ThrowHelperError( 
                                            new CorruptStoreException( 
                                                        SR.GetString( SR.StoreSignatureNotValid ) ) );
                } 

            }

            input.Seek( pos, SeekOrigin.Begin ); 
        }
 
        // 
        // Summary:
        //  Loads all data buffers from the specified stream. 
        //
        // Remarks:
        //
        // Parameters: 
        //  stream:     Data stream to use to initalize all buffers.
        // 
        protected void LoadFrom( Stream stream ) 
        {
            if( null == stream ) 
            {
                throw IDT.ThrowHelperArgumentNull( "stream" );
            }
 
            stream.Seek( 0, SeekOrigin.Begin );
 
            unsafe 
            {
                BinaryReader reader = new InfoCardBinaryReader( stream ); 
                {
                    m_header = new byte[ GetTotalHeaderSize() ];

                    // 
                    // Read the File Header.
                    // 
                    reader.Read( m_header, 0, m_header.Length ); 

                    // 
                    // Read the version
                    //
                    fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] )
                    { 
                        EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer;
 
                        // 
                        // First check the version info that it is supported.
                        // 
                        if ( FILEVERSION != pHeader->Version && FILEVERSIONV2 != pHeader->Version )
                        {
                            CorruptStoreException cse = new CorruptStoreException(
                                   SR.GetString( 
                                        SR.StoreVersionNotSupported,
                                        pHeader->Version ) ); 
 
                            IDT.TraceAndLogException( cse );
 
                            throw IDT.ThrowHelperError( cse );

                        }
                        if( FILEVERSIONV2 == pHeader->Version ) 
                        {
                            // 
                            // Since the stream is at the end of the header, we know 
                            // that the next chunk of data is the encryptedKey
                            m_encryptedKey = new byte[ pHeader->KeyBlockV2.KeyDataSize ]; 
                            reader.Read( m_encryptedKey, 0, pHeader->KeyBlockV2.KeyDataSize );
                        }

 
                    }
 
                    // 
                    // Populate the signature size
                    // 
                    fixed( Byte* pSignatureHeaderBuffer = &m_header[ 0 ] )
                    {
                        SignatureHeader* pSignatureHeader = (SignatureHeader*)pSignatureHeaderBuffer;
                        int inputSignatureLength = pSignatureHeader->SignatureSize; 

                        // 
                        // This code will go away 
                        //
                        if( m_variableSignatureLength != inputSignatureLength ) 
                        {
                            throw IDT.ThrowHelperError(
                                           new CorruptStoreException(
                                                       SR.GetString( SR.StoreSignatureNotValid ) ) ); 
                        }
 
                    } 

 
                    //
                    // Try to get the encryption key from the file header using
                    // DPAPI or the passphrase.  Will throw if invalid.
                    // 
                    ObtainDataKeyFromHeader();
 
                    // 
                    // Once the key has been obtained, we must ensure that the signature
                    // matches the data in the file. 
                    //
                    ValidateSignature( stream );

                    fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) 
                    {
                        EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer; 
 

 
                        byte[ ] iv = AllocateIvBuffer();
                        byte[ ] mIndex;
                        byte[ ] sIndex;
                        byte[ ] dBuffer; 
                        int secondaryIndexCount;
                        int secondaryIndexLastIndex; 
                        int masterIndexCount; 
                        int secondaryIndexSize;
 
                        //
                        // Read all of the secondaryindex information.
                        //
                        using( MemoryStream tempDataStream = new MemoryStream() ) 
                        {
                            // 
                            // read the IV used for encrypting the indexes. 
                            //
                            reader.Read( iv, 0, iv.Length ); 

                            //
                            // Decrypt the index information
                            // 
                            byte[ ] encryptedIndexBuffer = reader.ReadBytes( pHeader->IndexSize );
                            using( MemoryStream tempEncryptedStream = new MemoryStream( encryptedIndexBuffer ) ) 
                            { 
                                DecryptData(
                                        iv, 
                                        tempEncryptedStream,
                                        tempDataStream );

                                Array.Clear( encryptedIndexBuffer, 0, encryptedIndexBuffer.Length ); 
                            }
 
                            tempDataStream.Seek( 0, SeekOrigin.Begin ); 

                            BinaryReader indexReader = new InfoCardBinaryReader( tempDataStream ); 
                            {
                                masterIndexCount = indexReader.ReadInt32();

                                IDT.Assert( 
                                        masterIndexCount > 0,
                                        "MasterIndexCount Invalid" ); 
 
                                mIndex = indexReader.ReadBytes( masterIndexCount * sizeof( Int32 ) );
 
                                secondaryIndexCount = indexReader.ReadInt32();

                                IDT.Assert(
                                    (UInt32)secondaryIndexCount <= (UInt32)m_indexes.Count, 
                                    "SecondaryIndexCount Invalid" );
 
                                foreach( string key in m_indexes.InnerIndexes.Keys ) 
                                {
                                    secondaryIndexLastIndex = indexReader.ReadInt32(); 
                                    secondaryIndexSize = indexReader.ReadInt32();

                                    int newSize = Utility.CalculateIncreaseByPercent(
                                                secondaryIndexSize * sizeof( SecondaryIndexItem ), 
                                                sizeof( SecondaryIndexItem ),
                                                SecondaryIndexDefinition.GetByName( key ).GrowthFactor ); 
 
                                    sIndex = new byte[ newSize ];
 
                                    indexReader.Read( sIndex, 0, secondaryIndexSize * sizeof( SecondaryIndexItem ) );

                                    m_indexes.SetBuffer( key, sIndex, secondaryIndexLastIndex );
                                } 
                            }
                        } 
 
                        //
                        // Read the datablob section 
                        //
                        int dataBlobLength = Utility.CalculateIncreaseByPercent(
                            Convert.ToInt32( reader.BaseStream.Length - reader.BaseStream.Position ),
                            1, 
                            5 );
 
                        using( MemoryStream tempDataStream = new MemoryStream( dataBlobLength ) ) 
                        {
                            byte[ ] localBuffer = new byte[ 256 ]; 
                            int bytesRead = 0;
                            int totalBytesRead = 0;
                            do
                            { 
                                bytesRead = reader.Read( localBuffer, 0, localBuffer.Length );
                                tempDataStream.Write( localBuffer, 0, bytesRead ); 
                                totalBytesRead += bytesRead; 
                            } while( bytesRead == localBuffer.Length );
 
                            dBuffer = tempDataStream.GetBuffer();

                            if( 0 == totalBytesRead )
                            { 
                                dBuffer = new byte[ INITIALBLOBAREASIZE ];
                            } 
 
                            m_data = new IndexedDataBuffer( mIndex, dBuffer, pHeader->DataSize, this );
                        } 
                    }
                }
            }
        } 

        // 
        // Summary: 
        //  Do "atomic" swap of shadow with real file.
        // 
        protected virtual void SwapFileWithShadow()
        {
            CloseFiles();
 
            OnSwapFileWithShadow();
 
            try 
            {
                CreateDirAndFiles(); 
            }
            catch
            {
                CloseFiles(); 
                throw;
            } 
        } 

        // 
        // Summary:
        //  Encrypts and saves KSTORE to the file header using DPAPI
        //
        unsafe void EncryptAndSaveDPAPIKeyToHeader() 
        {
            // 
            // NOTE: These are all STACK structures, 
            // even though they have a new statement.
            // They must be assigned to something, or the compiler, 
            // will nto let nem be used as a ref.
            //
            DataBlob raw;
            DataBlob userEncrypted = new DataBlob(); 
            DataBlob doubleEncrypted = new DataBlob();
            byte[ ] dataToEncrypt = new byte[ ENCRYPTIONKEYBUFFERSIZE + HashUtility.HashBufferLength ]; 
 
            UnprotectKey();
            try 
            {
                //
                // Copy in the key
                // 
                Array.Copy( m_key, 0, dataToEncrypt, 0, ENCRYPTIONKEYBUFFERSIZE );
 
                // 
                // Append the hash of the key
                // 
                HashUtility.SetHashValue( dataToEncrypt, ENCRYPTIONKEYBUFFERSIZE, m_key, 0, ENCRYPTIONKEYBUFFERSIZE );

                fixed( Byte* pKey = &dataToEncrypt[ 0 ] )
                { 
                    //
                    // Capture the raw key 
                    // 
                    raw.pbData = new IntPtr( pKey );
                    raw.cbData = dataToEncrypt.Length; 
                    try
                    {
                        //
                        // We first protect the data using the users credentials. 
                        //
                        if( !NativeMethods.CryptProtectData( 
                                            new IntPtr( &raw ), 
                                            null,
                                            IntPtr.Zero, 
                                            IntPtr.Zero,
                                            IntPtr.Zero,
                                            NativeMethods.CRYPTPROTECT_UI_FORBIDDEN | NativeMethods.CRYPTPROTECT_VERIFY_PROTECTION,
                                            new IntPtr( &userEncrypted ) ) ) 
                        {
                            throw IDT.ThrowHelperError( new Win32Exception( 
                                        Marshal.GetLastWin32Error(), 
                                        SR.GetString( SR.StoreCryptProtectDataFailed ) ) );
                        } 

                        using( SystemIdentity lsa = new SystemIdentity( true ) )
                        {
                            if( !NativeMethods.CryptProtectData( 
                                                    new IntPtr( &userEncrypted ),
                                                    null, 
                                                    IntPtr.Zero, 
                                                    IntPtr.Zero,
                                                    IntPtr.Zero, 
                                                    NativeMethods.CRYPTPROTECT_UI_FORBIDDEN | NativeMethods.CRYPTPROTECT_VERIFY_PROTECTION,
                                                    new IntPtr( &doubleEncrypted ) ) )
                            {
                                throw IDT.ThrowHelperError( new Win32Exception( 
                                    Marshal.GetLastWin32Error(),
                                    SR.GetString( SR.StoreCryptProtectDataAsSystemFailed ) ) ); 
                            } 
                        }
 
                        fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] )
                        {
                            EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer;
 
                            //
                            // Set the KeyBlockV2 fields 
                            // 
                            pHeader->KeyBlockV2.KeyDataOffset = 0;
                            pHeader->KeyBlockV2.KeyDataSize = doubleEncrypted.cbData; 

                            m_encryptedKey = new byte[ doubleEncrypted.cbData ];

                            // 
                            // Copy the encrypted key to the member
                            // 
                            for( int i = 0; i < doubleEncrypted.cbData; i++ ) 
                            {
                               m_encryptedKey[ i ] = ( (Byte*)doubleEncrypted.pbData.ToPointer() )[ i ]; 
                            }

                        }
                    } 
                    finally
                    { 
                        Exception e = null; 
                        if( IntPtr.Zero != userEncrypted.pbData )
                        { 
                            Utility.ClearUnsafeMemory(
                                        userEncrypted.pbData,
                                        userEncrypted.cbData );
                            if( IntPtr.Zero != NativeMethods.LocalFree( userEncrypted.pbData ) ) 
                            {
                                e = new Win32Exception( Marshal.GetLastWin32Error() ); 
                            } 
                        }
 
                        if( IntPtr.Zero != doubleEncrypted.pbData )
                        {
                            Utility.ClearUnsafeMemory(
                                        doubleEncrypted.pbData, 
                                        doubleEncrypted.cbData );
                            if( IntPtr.Zero != NativeMethods.LocalFree( doubleEncrypted.pbData ) ) 
                            { 
                                //
                                // We potentially overwrite here, but will always report one of the 
                                // exceptions.
                                //
                                e = new Win32Exception( Marshal.GetLastWin32Error() );
                            } 
                        }
                        if( null != e ) 
                        { 
                            throw IDT.ThrowHelperError( e );
                        } 
                    }
                }
            }
            finally 
            {
                ProtectKey(); 
                Array.Clear( 
                         dataToEncrypt,
                         0, 
                         dataToEncrypt.Length );
            }
        }
 
        //
        // Summary: 
        //  Reads the key from the header file, base on the state 
        //  of the object.
        // 
        void ObtainDataKeyFromHeader()
        {
            try
            { 
                byte[ ] key = TryObtainDataKeyFromDPAPI();
 
                if( null == key ) 
                {
                    throw IDT.ThrowHelperError( new InvalidOperationException( 
                                    SR.GetString( SR.StoreUnableToGetStoreKeyFromDPAPI ) ) );
                }

                m_key = key; 
                m_keyProtected = false;
 
                ProtectKey(); 
            }
            catch( Exception e ) 
            {
                if( IDT.IsFatal( e ) )
                {
                    throw; 
                }
                throw IDT.ThrowHelperError( new InvalidStoreProtectionKeyException() ); 
            } 

        } 

        //
        // Summary:
        //  Trys to obtain KSTORE using DPAPI 
        //
        // Returns: 
        //  KSTORE on success, null on failure 
        //
        unsafe byte[ ] TryObtainDataKeyFromDPAPI() 
        {
            //
            // We do double encryption here.
            // First, we should be running as System, 
            // so our initial call to DPAPI will run as System.
            // We then need to decrypt using the Users context, 
            // this reduces the chance of a single key exposing all 
            // user data.
            // 
            DataBlob raw = new DataBlob();
            DataBlob userEncrypted = new DataBlob();

            // not an out param on a method, so we don't have to explicity ctor it. 
            DataBlob doubleEncrypted;
 
            fixed( Byte* pHeaderBuffer = &m_header[ GetOffsetToEncryptedFileHeader() ] ) 
            {
                EncryptedFileStoreHeader* pHeader = (EncryptedFileStoreHeader*)pHeaderBuffer; 

                if( FILEVERSION == pHeader->Version && pHeader->KeyBlock.KeyType != 0 )
                {
                    throw IDT.ThrowHelperError( new InvalidOperationException( 
                                    SR.GetString( SR.StoreFileNotProtectedWithDPAPI ) ) );
                } 
 
                //
                // Managed members for output and verificaiton 
                //
                byte[ ] key = null;
                byte[ ] keyVerify = null;
                GCHandle gch = new GCHandle(); 
                try
                { 
                    // 
                    // Switch on the Fileversion on the header type to read
                    // the encrypted key 
                    //
                    if( pHeader->Version == FILEVERSIONV2 )
                    {
                        gch = GCHandle.Alloc( m_encryptedKey, GCHandleType.Pinned ); 
                        doubleEncrypted.pbData = gch.AddrOfPinnedObject();
                        doubleEncrypted.cbData = pHeader->KeyBlockV2.KeyDataSize; 
 
                    }
                    else 
                    {
                        doubleEncrypted.pbData = new IntPtr( &pHeader->KeyBlock.EncryptedKey );
                        doubleEncrypted.cbData = pHeader->KeyBlock.EncryptedKeySize;
 
                        //
                        // Upgrade Scenario: We have read the key from the v1 format, we need to 
                        // store the key in the v2 format from now on. 
                        //
                        // 
                        // Set the KeyBlockV2 fields
                        //
                        pHeader->KeyBlockV2.KeyDataOffset = 0;
                        pHeader->KeyBlockV2.KeyDataSize = doubleEncrypted.cbData; 

                        m_encryptedKey = new byte[ doubleEncrypted.cbData ]; 
 
                        //
                        // Copy the encrypted key to the member 
                        //
                        for( int i = 0; i < doubleEncrypted.cbData; i++ )
                        {
                            m_encryptedKey[ i ] = ( (Byte*)doubleEncrypted.pbData.ToPointer() )[ i ]; 
                        }
 
                    } 

                    using( SystemIdentity lsa = new SystemIdentity( true ) ) 
                    {
                        //
                        // Decrypt using System.
                        // 
                        if( !NativeMethods.CryptUnprotectData(
                                            new IntPtr( &doubleEncrypted ), 
                                            null, 
                                            IntPtr.Zero,
                                            IntPtr.Zero, 
                                            IntPtr.Zero,
                                            NativeMethods.CRYPTPROTECT_UI_FORBIDDEN,
                                            new IntPtr( &userEncrypted ) ) )
                        { 
                            throw IDT.ThrowHelperError( new Win32Exception(
                                        Marshal.GetLastWin32Error(), 
                                        SR.GetString( SR.StoreCryptUnprotectDataAsSystemFailed ) ) ); 
                        }
                    } 

                    //
                    // Decrypt using the Store key as the user.
                    // 
                    if( !NativeMethods.CryptUnprotectData(
                                        new IntPtr( &userEncrypted ), 
                                        null, 
                                        IntPtr.Zero,
                                        IntPtr.Zero, 
                                        IntPtr.Zero,
                                        NativeMethods.CRYPTPROTECT_UI_FORBIDDEN,
                                        new IntPtr( &raw ) ) )
                    { 
                        throw IDT.ThrowHelperError( new Win32Exception(
                                Marshal.GetLastWin32Error(), 
                                SR.GetString( SR.StoreCryptUnprotectDataFailed ) ) ); 
                    }
 
                    //
                    // Ensure that we have the correct size
                    //
                    IDT.Assert( 
                        raw.cbData == ( ENCRYPTIONKEYBUFFERSIZE + HashUtility.HashBufferLength ),
                        "The data size returned by CryptUnprotectData is invalid or corrupt." ); 
 
                    key = new byte[ ENCRYPTIONKEYBUFFERSIZE ];
                    Marshal.Copy( 
                            raw.pbData,
                            key,
                            0,
                            ENCRYPTIONKEYBUFFERSIZE ); 
                    //
                    // Calc a hash of the first part of the output. 
                    // 
                    keyVerify = new byte[ HashUtility.HashBufferLength ];
 
                    HashUtility.SetHashValue(
                                    keyVerify,
                                    0,
                                    key, 
                                    0,
                                    ENCRYPTIONKEYBUFFERSIZE ); 
 
                    //
                    // Verify the hash against the hash in the encrypted blob, 
                    // if they are different, then our files are invalid.
                    //
                    for( int i = 0; i < keyVerify.Length; i++ )
                    { 
                        if( keyVerify[ i ] != ( (Byte*)raw.pbData.ToPointer() )[ ENCRYPTIONKEYBUFFERSIZE + i ] )
                        { 
                            throw IDT.ThrowHelperError( new InvalidOperationException( 
                                            SR.GetString( SR.StoreDecryptedKeyIsNotValid ) ) );
                        } 
                    }

                    return key;
                } 
                catch
                { 
                    if( null != key ) 
                    {
                        Array.Clear( 
                                key,
                                0,
                                key.Length );
                    } 
                    throw;
                } 
                finally 
                {
                    // 
                    // Free the GCHandle
                    //
                    if( gch.IsAllocated )
                    { 
                        gch.Free();
                    } 
 
                    if( IntPtr.Zero != userEncrypted.pbData )
                    { 
                        Utility.ClearUnsafeMemory( userEncrypted.pbData, userEncrypted.cbData );
#pragma warning suppress 56523 // return value ignored
                        NativeMethods.LocalFree( userEncrypted.pbData );
 
                    }
 
                    if( IntPtr.Zero != raw.pbData ) 
                    {
                        Utility.ClearUnsafeMemory( raw.pbData, raw.cbData ); 
#pragma warning suppress 56523  // return value ignored.
                        NativeMethods.LocalFree( raw.pbData );

                    } 

                } 
            } 
        }
 
        //
        // Summary:
        // Returns true & Log if acl associated with FILE/DIRECTORY has been modified/tampered with
        // 
        // Arguments:
        // fileSysInfo - the file or dir we want to check 
        // 
        bool LogIfAclsTampered( FileSystemInfo fileSysInfo )
        { 

            bool tampered = true;
            try
            { 
                FileSystemSecurity fs = null;
 
                // 
                // Get the ACL based on whether we're dealing with dir or file.
                // 
                if( fileSysInfo is DirectoryInfo )
                {
                    fs = ( (DirectoryInfo)fileSysInfo ).GetAccessControl( AccessControlSections.Access | AccessControlSections.Owner );
 
                }
                else 
                { 
                    IDT.Assert( fileSysInfo is FileInfo, "Only fileinfo possible" );
                    fs = ( (FileInfo)fileSysInfo ).GetAccessControl( AccessControlSections.Access | AccessControlSections.Owner ); 

                }

                // 
                // Lets do the check
                // 
                tampered = LogIfAclsTamperedHelper( fs ); 

            } 
            catch( UnauthorizedAccessException )
            {
                // tampered with for sure since we are not even able to access it
                IDT.Assert( tampered, "ACL must have been tampered with" ); 

            } 
 
            if( tampered )
            { 
                DataAccessException dae = new DataAccessException( SR.GetString( SR.StoreAclsTamperedWith ) );
                IDT.TraceAndLogException( dae );
            }
 
            return tampered;
        } 
 
        //
        // Summary: 
        // Returns true if acl modified/tampered with
        //
        // Arguments:
        // fs - the acl of the file or dir we want to check 
        //
        bool LogIfAclsTamperedHelper( FileSystemSecurity fs ) 
        { 
            //
            // Check owner 
            //
            if( SystemIdentity.LsaIdentityReference !=
                fs.GetOwner( typeof( SecurityIdentifier ) ) )
            { 
                return true;
            } 
 
            //
            // Check the rules 
            //
            AuthorizationRuleCollection rules = fs.GetAccessRules( true, true, typeof( SecurityIdentifier ) );

            // 
            // For files only system is allowed ( and with full control )
            // for the directory we grant admins the right to delete the directory and files within. 
            // 
            bool isDirectory = fs is DirectorySecurity;
 
            foreach( FileSystemAccessRule rule in rules )
            {
                if( ( SystemIdentity.LsaIdentityReference               == rule.IdentityReference &&
                            FileSystemRights.FullControl                == rule.FileSystemRights && 
                            AccessControlType.Allow                     == rule.AccessControlType  )
                    || 
                     ( isDirectory && 
                        AdministratorSid                                == rule.IdentityReference &&
                        AccessControlType.Allow                         == rule.AccessControlType && 
                        ( FileSystemRights.DeleteSubdirectoriesAndFiles
                        | FileSystemRights.ReadData
                        | FileSystemRights.Synchronize )                == rule.FileSystemRights ) )
                { 
                    // allowed
                } 
                else 
                {
                    return true; 
                }
            }

            return false; 
        }
 
        static T CreateSecurityDescriptor() 
            where T :FileSystemSecurity, new()
        { 
            T sec = new T();
            sec.SetOwner( SystemIdentity.LsaIdentityReference );

            AuthorizationRuleCollection rules = sec.GetAccessRules( true, true, typeof( SecurityIdentifier ) ); 
            foreach( FileSystemAccessRule rule in rules )
            { 
                sec.RemoveAccessRuleAll( rule ); 
            }
 
            sec.AddAccessRule( new FileSystemAccessRule(
                                                SystemIdentity.LsaIdentityReference,
                                                FileSystemRights.FullControl,
                                                InheritanceFlags.None, 
                                                PropagationFlags.None,
                                                AccessControlType.Allow ) ); 
 
            if( sec is DirectorySecurity )
            { 
                sec.AddAccessRule( new FileSystemAccessRule(
                                        AdministratorSid                                ,
                                        FileSystemRights.DeleteSubdirectoriesAndFiles
                                        | FileSystemRights.ListDirectory, 
                                        AccessControlType.Allow ) );
 
            } 
            sec.SetAccessRuleProtection( true, false );//protected and no inheritance.
            return sec; 
        }


        private void SetupProvider() 
        {
            m_provider = SafeRsaProviderHandle.Construct(); 
        } 

        private byte[ ] AllocateIvBuffer() 
        {
            IDT.Assert( SignatureHeader.IVSize == m_encAlg.IV.Length, "Must be equal" );
            IDT.Assert( ENCRYPTIONBLOCKBITLENGTH / 8 == m_encAlg.IV.Length, "Must be equal" );
            return new byte[ m_encAlg.IV.Length ]; 
        }
 
        // 
        // Summary:
        //    Attempts to delete a file using exponential backoff to cope 
        //    with other processes temporarily locking the data.
        // parameters:
        //  toDelete  - the file to delete
        // 
        static private void ResilientDelete( FileInfo toDelete )
        { 
            const int MAX_TIME     = 500; // milliseconds 
            const int START_TIME   = 10;  // milliseconds
 
            int current = START_TIME;
            bool succeeded = true;
            Exception toReport = null;
            do 
            {
                if( !succeeded ) 
                { 
                    Thread.Sleep( current );
                    current *= 2; 
                }

                try
                { 
                     toDelete.Delete();
                     succeeded = true; 
                } 
                catch( IOException ioe )
                { 
                    IDT.TraceDebug( "Failed to delete {0} with error {1}\nDelay is now {2}\n",
                                    toDelete.FullName,
                                    ioe.Message,
                                    current ); 
                    toReport = ioe;
                    succeeded = false; 
                } 
            } while( !succeeded && current <= MAX_TIME );
 
            //
            // If we fail here the rollback logic should apply.
            //
            if( !succeeded ) 
            {
                throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreFileInUse ), toReport ) ); 
            } 
        }
        // 
        // Summary:
        //    Attempts to move a file using exponential backoff to cope
        //    with other processes temporarily locking the data.
        // parameters: 
        //  fileFrom - the file we are moving from
        //  fileTo - the file we are moving to. 
        // 
        static private void ResilientMove( FileInfo fileFrom, FileInfo fileTo )
        { 
            const int MAX_TIME     = 500; // milliseconds
            const int START_TIME   = 10;  // milliseconds

            int current = START_TIME; 
            bool succeeded = true;
            Exception toReport = null; 
            do 
            {
                if( !succeeded ) 
                {
                    Thread.Sleep( current );
                    current *= 2;
                } 

                try 
                { 

                     fileFrom.MoveTo( fileTo.FullName ); 
                     succeeded = true;
                }
                catch( IOException ioe )
                { 
                    IDT.TraceDebug( "Failed to move {0} to {1} with error {2}\nDelay is now {3}\n",
                                    fileFrom.FullName, 
                                    fileTo.FullName, 
                                    ioe.Message,
                                    current ); 
                    toReport = ioe;
                    succeeded = false;
                }
            } while( !succeeded && current <= MAX_TIME ); 

            // 
            // There is not much we can do here, as the commit operation is not currently atomic. We 
            // will failfast the service and tell the user why we had to do so.
            // 
            if( !succeeded )
            {
                IDT.FailFast( SR.GetString( SR.StoreFileInUse ) );
            } 
        }
 
        // 
        // Summary:
        //    Attempts to open a file using exponential backoff to cope 
        //    with other processes temporarily locking the data.
        // parameters:
        //  toOpen - the file we are looking to open
        //  mode   - the mode the file is to be openned in 
        //  access - the appropriate access to be granted.
        // 
        static private FileStream ResilientOpen( FileInfo toOpen, FileMode mode, FileAccess access ) 
        {
            const int MAX_TIME     = 500; // milliseconds 
            const int START_TIME   = 10;  // milliseconds

            int current = START_TIME;
            bool succeeded = true; 
            Exception toReport = null;
            FileStream returnStream = null; 
            do 
            {
                if( !succeeded ) 
                {
                    Thread.Sleep( current );
                    current *= 2;
                } 

                try 
                { 
                     returnStream = toOpen.Open( mode, access, FileShare.None );
                     succeeded = true; 
                }
                catch( IOException ioe )
                {
                    IDT.TraceDebug( "Failed to open {0} with error {1}\nDelay is now {2}\n", 
                                    toOpen.FullName,
                                    ioe.Message, 
                                    current ); 
                    toReport = ioe;
                    succeeded = false; 
                }
            } while( !succeeded && current <= MAX_TIME );

            // 
            // If we fail here then tell the user that the store files are in use.
            // 
            if( !succeeded ) 
            {
                throw IDT.ThrowHelperError( new DataAccessException( SR.GetString( SR.StoreFileInUse ), toReport ) ); 
            }
            return returnStream;
        }
 
        //
        // Summary: 
        //  Creates the encryption alg we use. 
        //
        internal static SymmetricAlgorithm CreateEncryptionAlg() 
        {
            RijndaelManaged r = new RijndaelManaged();
            r.Mode = CipherMode.CBC;
            r.KeySize = ENCRYPTIONKEYBITLENGTH; 
            r.BlockSize = ENCRYPTIONBLOCKBITLENGTH;
            return r; 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK