RightsManagementEncryptionTransform.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Base / MS / Internal / IO / Packaging / CompoundFile / RightsManagementEncryptionTransform.cs / 1305600 / RightsManagementEncryptionTransform.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//  This class implements the RM data transform for a compound file. 
//
// History: 
//  06/03/2002: IgorBel:    Initial implementation.
//  08/15/2002: LGolding:   In the 07/17/2002 drop of the RM SDK, the server has
//                              changed from ULTNGSTN01 to TungstenTest07, and the
//                              activation URL has changed. 
//  05/29/2003: LGolding:   Ported to WCP tree.
//  06/10/2003: IgorBel:    Ported to Krypton APIs 
//  04/07/2005: LGolding:   Ported to managed wrappers around Promethium APIs. 
//
//----------------------------------------------------------------------------- 

// Allow use of presharp warning numbers [6518] unknown to the compiler
#pragma warning disable 1634, 1691
 
using System;
using System.Collections; 
using System.Collections.Generic; 
using System.Diagnostics;
using System.Globalization; 
using System.IO;
using System.IO.Packaging;
using System.Text;
using System.Windows; 
using System.Security.RightsManagement;
 
using MS.Internal.IO.Packaging.CompoundFile; 
using MS.Internal.Utility;
 
using CU = MS.Internal.IO.Packaging.CompoundFile.ContainerUtilities;
using MS.Internal.WindowsBase;

namespace MS.Internal.IO.Packaging.CompoundFile 
{
    ///  
    /// This class implements the IDataTransform interface for the transform that 
    /// implements RM encryption in a compound file.
    ///  
    internal class RightsManagementEncryptionTransform : IDataTransform
    {
        //-----------------------------------------------------
        // 
        //  Constructors
        // 
        //----------------------------------------------------- 

        #region Constructors 

        /// 
        /// 
        /// Constructor. 
        /// 
        ///  
        /// Every transform class is required to have a constructor with this signature. 
        /// The compound file code invokes this constructor via reflection for each transform
        /// when it builds the transform stack for a dataspace. The transform object will 
        /// use the properties of the  parameter
        /// to locate and extract the transform's "instance data" from the compound file.
        /// 
        ///  
        /// The instance data for a RightsManagementEncryptionTransform consists of a
        /// PublishLicense object and zero or more UseLicense objects, each associated with 
        /// a user. 
        /// 
        ///  
        internal
        RightsManagementEncryptionTransform(
            TransformEnvironment transformEnvironment
            ) 
        {
            Debug.Assert(transformEnvironment != null); 
 
            Stream instanceDataStream = transformEnvironment.GetPrimaryInstanceData();
 
            Debug.Assert(instanceDataStream != null, SR.Get(SRID.NoPublishLicenseStream));

            _useLicenseStorage = transformEnvironment.GetInstanceDataStorage();
 
            Debug.Assert(_useLicenseStorage != null, SR.Get(SRID.NoUseLicenseStorage));
 
            // Create a wrapper that manages persistence and comparison of FormatVersion 
            // in our InstanceData stream.  We can read/write to this stream as needed (though CompressionTransform
            // does not because we don't house any non-FormatVersion data in the instance data stream). 
            // We need to give out our current code version so it can compare with any file version as appropriate.
            _publishLicenseStream = new VersionedStreamOwner(
                instanceDataStream,
                new FormatVersion(FeatureName, MinimumReaderVersion, MinimumUpdaterVersion, CurrentFeatureVersion)); 
        }
 
        #endregion Constructors 

        //------------------------------------------------------ 
        //
        //  Public Methods
        //
        //----------------------------------------------------- 

        #region Public Methods 
 
        /// 
        /// Read the publish license from the RM transform's primary instance data stream. 
        /// 
        /// 
        /// The publish license, or null if the compound file does not contain a publish
        /// license (as it will not, for example, when the compound file is first created). 
        /// 
        ///  
        /// If the stream is corrupt, or if the RM instance data in this file cannot be 
        /// read by the current version of this class.
        ///  
        internal PublishLicense
        LoadPublishLicense()
        {
            if (_publishLicenseStream.Length <= 0) 
                return null;
 
            // We seek to position 0 but under the covers, the VersionedStream maintains a FormatVersion 
            // structure before our logical position zero.
            _publishLicenseStream.Seek(0, SeekOrigin.Begin); 

            //
            // Construct a BinaryReader to read the rest of the instance data.
            // 
            // Although BinaryReader is IDisposable, we must not Close or Dispose it,
            // as that would close the underlying stream, which we do not own. Simply 
            // allowing the BinaryReader to be finalized after it goes out of scope 
            // does -not- close the underlying stream.
            // 

// Suppress 6518 Local IDisposable object not disposed:
// Reason: The stream is not owned by the BlockManager, therefore we cannot
// close the BinaryWriter, as that would Close the stream underneath. 
#pragma warning disable 6518
            BinaryReader utf8Reader = new BinaryReader(_publishLicenseStream, Encoding.UTF8); 
#pragma warning restore 6518 

            // 
            // There follows a variable-length header (not to be confused with the physical
            // stream header). This header allows future expansion, in case we want to store
            // something in addition to the publish license in the primary instance data stream
            // for this transform. The first field in the header is the header length in bytes 
            // (including the headerLen field itself).
            // 
            Int32 headerLen = utf8Reader.ReadInt32(); 
            if (headerLen < CU.Int32Size)
            { 
                throw new FileFormatException(SR.Get(SRID.PublishLicenseStreamCorrupt));
            }

            if (headerLen > MaxPublishLicenseHeaderLen) 
            {
                throw new FileFormatException( 
                                SR.Get(SRID.PublishLicenseStreamHeaderTooLong, 
                                headerLen,
                                MaxPublishLicenseHeaderLen 
                                ));
            }

            // 
            // Save any additional bytes in the header that we don't recognize, so we can
            // write them back out later if necessary. We've already read the headerLen field, 
            // so subtract the size of that field from the amount we have to save. 
            //
            // No need to use checked{} here since we already made sure that header length is greater than Int32Size 
            Int32 numPublishLicenseHeaderExtraBytes = headerLen - CU.Int32Size;
            if (numPublishLicenseHeaderExtraBytes > 0)
            {
                _publishLicenseHeaderExtraBytes = new byte [numPublishLicenseHeaderExtraBytes]; 
                if (PackagingUtilities.ReliableRead(_publishLicenseStream, _publishLicenseHeaderExtraBytes, 0, numPublishLicenseHeaderExtraBytes)
                        != numPublishLicenseHeaderExtraBytes) 
                { 
                    throw new FileFormatException(SR.Get(SRID.PublishLicenseStreamCorrupt));
                } 
            }

            //
            // Read the publish license as a length-prefixed UTF-8 string. If the stream 
            // is shorter than the length prefix implies, an exception will be thrown.
            // 
            _publishLicense = new PublishLicense( 
                                        ReadLengthPrefixedString(
                                            utf8Reader, 
                                            Encoding.UTF8,
                                            PublishLicenseLengthMax
                                            )
                                        ); 

            return _publishLicense; 
        } 

        ///  
        /// Save the publish license to the RM transform's instance data stream.
        /// 
        /// 
        /// The publish licence to be saved. The RM server returns a publish license as a string. 
        /// 
        ///  
        /// The stream is rewritten from the beginning, so any existing publish license is 
        /// overwritten.
        ///  
        /// 
        /// If  is null.
        /// 
        ///  
        /// If the existing RM instance data in this file cannot be updated by the current version
        /// of this class. 
        ///  
        /// 
        /// If the transform settings are fixed. 
        /// 
        internal void
        SavePublishLicense(
            PublishLicense publishLicense 
            )
        { 
            if (publishLicense == null) 
            {
                throw new ArgumentNullException("publishLicense"); 
            }

            if (_fixedSettings)
            { 
                throw new InvalidOperationException(SR.Get(SRID.CannotChangePublishLicense));
            } 
 
            // We seek to position 0 but under the covers, the VersionedStream maintains a FormatVersion
            // structure before our logical position zero. 
            _publishLicenseStream.Seek(0, SeekOrigin.Begin);

            //
            // Construct a BinaryWriter to write the rest of the instance data. 
            //
            // Although BinaryWriter is IDisposable, we must not Close or Dispose it, 
            // as that would close the underlying stream, which we do not own. Simply 
            // allowing the BinaryWriter to be finalized after it goes out of scope
            // does -not- close the underlying stream. 
            //

// Suppress 6518 Local IDisposable object not disposed:
// Reason: The stream is not owned by the BlockManager, therefore we cannot 
// close the BinaryWriter, as that would Close the stream underneath.
#pragma warning disable 6518 
            BinaryWriter utf8Writer = new BinaryWriter(_publishLicenseStream, Encoding.UTF8); 
#pragma warning restore 6518
 
            //
            // There follows a variable-length header (not to be confused with the physical
            // stream header). This header allows future expansion, in case we want to store
            // something in addition to the publish license in the primary instance data stream 
            // for this transform. In this version, there is no additional information, so the
            // header consists only of the headerLen field itself, so its length is 4 bytes. 
            // 
            //
            // If we have previously read in the publish license stream from a file whose format 
            // included extra header bytes that we didn't interpret, write those bytes back out
            // (and include them in the header length).
            //
            Int32 headerLen = CU.Int32Size; 
            if (_publishLicenseHeaderExtraBytes != null)
            { 
                checked { headerLen += _publishLicenseHeaderExtraBytes.Length; } 
            }
            utf8Writer.Write(headerLen); 

            if (_publishLicenseHeaderExtraBytes != null)
            {
                _publishLicenseStream.Write( 
                    _publishLicenseHeaderExtraBytes,
                    0, 
                    _publishLicenseHeaderExtraBytes.Length 
                    );
            } 

            //
            // Write out the publish license as a length-prefixed, UTF-8 encoded string.
            // 
            WriteByteLengthPrefixedDwordPaddedString(publishLicense.ToString(), utf8Writer, Encoding.UTF8);
 
            utf8Writer.Flush(); 

            _publishLicense = publishLicense; 
        }

        /// 
        /// Load a use license for the specified user from the RM transform's instance data 
        /// storage in the compound file.
        ///  
        ///  
        /// The user whose use license is desired.
        ///  
        /// 
        /// The use license for the specified user, or null if the compound file does not
        /// contain a use license for the specified user.
        ///  
        /// 
        /// If  is null. 
        ///  
        /// 
        /// If the RM information in this file cannot be read by the current version of 
        /// this class.
        /// 
        internal UseLicense
        LoadUseLicense( 
            ContentUser user
            ) 
        { 
            if (user == null)
            { 
                throw new ArgumentNullException("user");
            }

            LoadUseLicenseForUserParams param = new LoadUseLicenseForUserParams(user); 

            EnumUseLicenseStreams( 
                new UseLicenseStreamCallback(this.LoadUseLicenseForUser), 
                param
                ); 

            return param.UseLicense;
        }
 
        /// 
        /// Save a use license for the specified user into the RM transform's instance data 
        /// storage in the compound file. 
        /// 
        ///  
        /// The user to whom the use license was issued.
        /// 
        /// 
        /// The use license issued to that user. 
        /// 
        ///  
        /// Any existing use license for the specified user is removed from the compound 
        /// file before the new use license is saved.
        ///  
        /// 
        /// If  or  is null.
        /// 
        ///  
        /// If the RM information in this file cannot be written by the current version of
        /// this class. 
        ///  
        internal void
        SaveUseLicense( 
            ContentUser user,
            UseLicense useLicense
            )
        { 
            if (user == null)
            { 
                throw new ArgumentNullException("user"); 
            }
 
            if (useLicense == null)
            {
                throw new ArgumentNullException("useLicense");
            } 

            if (user.AuthenticationType != AuthenticationType.Windows && 
                user.AuthenticationType != AuthenticationType.Passport) 
            {
                throw new ArgumentException( 
                    SR.Get(SRID.OnlyPassportOrWindowsAuthenticatedUsersAreAllowed),
                    "user"
                    );
            } 

            // 
            // Delete any existing use license for this user. 
            //
            EnumUseLicenseStreams( 
                new UseLicenseStreamCallback(this.DeleteUseLicenseForUser),
                user
                );
 
            //
            // Save the new use license for this user in a new stream. 
            // 
            SaveUseLicenseForUser(user, useLicense);
        } 

        /// 
        /// Delete the use license for the specified user from the RM transform's instance
        /// data storage in the compound file. 
        /// 
        ///  
        /// The user whose use license is to be deleted. 
        /// 
        ///  
        /// If  is null.
        /// 
        /// 
        /// If the RM information in this file cannot be updated by the current version of 
        /// this class.
        ///  
        internal void 
        DeleteUseLicense(
            ContentUser user 
            )
        {
            if (user == null)
            { 
                throw new ArgumentNullException("user");
            } 
 
            EnumUseLicenseStreams(
                new UseLicenseStreamCallback(this.DeleteUseLicenseForUser), 
                user
                );
        }
 
        /// 
        /// This method retrieves a reference to a dictionary with keys of type User and values 
        /// of type UseLicense, containing one entry for each use license embedded in the compound 
        /// file for this particular transform instance. The collection is a snapshot of the use
        /// licenses in the compound file at the time of the call. The term "Embedded" in the method 
        /// name emphasizes that the dictionary returned by this method only includes those use
        /// licenses that are embedded in the compound file. It does not include any other use
        /// licenses that the application might have acquired from an RM server but not yet embedded
        /// into the  compound file. 
        /// 
        internal IDictionary 
        GetEmbeddedUseLicenses() 
        {
            UserUseLicenseDictionaryLoader loader = new UserUseLicenseDictionaryLoader (this); 
            return new ReadOnlyDictionary(loader.LoadedDictionary);
        }

        #endregion Public Methods 

        //------------------------------------------------------ 
        // 
        //  Public Properties
        // 
        //------------------------------------------------------

        #region Public Properties
 
        #region IDataTransform Properties
 
        ///  
        /// Returns a value indicating whether this transform is ready for use.
        ///  
        public bool IsReady
        {
            get
            { 
                return      (_cryptoProvider != null)
                            && 
                                (_cryptoProvider.CanDecrypt ||_cryptoProvider.CanEncrypt); 
            }
        } 

        /// 
        /// 
        /// Returns true when the transform expects no further changes to its state. 
        /// The contract is that if FixedSettings is false, an application can change
        /// any of the transform�s properties with the promise that an exception will 
        /// not be thrown. 
        /// 
        ///  
        /// For the RightsManagementEncryptionTransform, FixedSettings becomes true the
        /// first time the compound file code calls the object�s GetTransformedStream method.
        /// After that, any attempt to set the CryptoProvider property, or to call
        /// SavePublicLicense, throws InvalidOperationException. 
        /// 
        ///  
        public bool FixedSettings 
        {
            get 
            {
                return _fixedSettings;
            }
        } 

        ///  
        /// Returns a value that tells the Data Space Manager how to interpret the 
        /// value of this transform's TransformIdentifierProperty.
        ///  
        /// 
        /// The original design of the Data Space Manager allowed for 3rd parties to
        /// implement their own transform classes. These transforms might be identified
        /// by the assembly-qualified name of the managed class that implements the 
        /// transform, or by the CLSID of the COM object that implements the transform.
        /// A transform's TransformIdentifierType property was intended to tell the Data 
        /// Space Manager whether the transform's TransformIdentifier property was to 
        /// be interpreted as a managed class name, a CLSID, or something else. The only
        /// value of TransformIdentifierType that the Data Space Manager actually supports 
        /// is TransformIdentifierTypes_PredefinedTransformName, which tells the Data
        /// Space Manager that the TransformIdentifier is one of a small number of well-
        /// known strings that identify the built-in transforms (compression and encryption).
        ///  
        internal int TransformIdentifierType
        { 
            get 
            {
                return DataSpaceManager.TransformIdentifierTypes_PredefinedTransformName; 
            }
        }

        ///  
        /// Returns a value that identifies the transform implemented by this object.
        ///  
        public object TransformIdentifier 
        {
            get 
            {
                return RightsManagementEncryptionTransform.ClassTransformIdentifier;
            }
        } 

        #endregion IDataTransform Properties 
 
        #region RightsManagementEncryptionTransform Properties
 
        /// 
        /// This property represents the CryptoProvider object that will be used to determine
        /// what operations the current user is allowed to perform on the encrypted content.
        ///  
        internal CryptoProvider CryptoProvider
        { 
            get 
            {
                return _cryptoProvider; 
            }
            set
            {
                if (_fixedSettings) 
                {
                    throw new InvalidOperationException(SR.Get(SRID.CannotChangeCryptoProvider)); 
                } 

                if (value == null) 
                {
                    throw new ArgumentNullException("value");
                }
 
                if (!value.CanEncrypt && !value.CanDecrypt)
                { 
                    throw new ArgumentException(SR.Get(SRID.CryptoProviderIsNotReady), "value"); 
                }
 
                _cryptoProvider = value;
            }
        }
 
        /// 
        /// Expose the transform identifier for the use of the DataSpaceManager. 
        ///  
        internal static string ClassTransformIdentifier
        { 
            get
            {
                return "{C73DFACD-061F-43B0-8B64-0C620D2A8B50}";
            } 
        }
 
        #endregion RightsManagementEncryptionTransform Properties 

        #endregion Public Properties 

        //-----------------------------------------------------
        //
        // Interface Implementation Methods 
        //
        //------------------------------------------------------ 
 
        #region Interface Implementation Methods
 
        /// 
        /// Creates a stream into which the compound file code will write cleartext bytes,
        /// and from which it will read cleartext bytes. When the compound file code writes
        /// bytes to this stream, they will be encrypted and then written to the underlying 
        /// stream (encodedStream). When the compound file code reads bytes from this
        /// stream, they will be read from the underlying stream and then decrypted. 
        ///  
        /// 
        /// The underlying stream, into which encrypted bytes are written and from which 
        /// encrypted bytes are read.
        /// 
        /// 
        /// No longer used. In the past, this dictionary was used to communicate information 
        /// such as the contents of the publish license to clients. Arbitrary key/value pairs
        /// could be written into the dictionary. The Stream-derived class returned by this 
        /// method was expected to expose IDictionary, and the dictionary methods were expected 
        /// to access the information in the transformContext. This mechanism is no longer needed.
        /// The POR is that this parameter will be removed. 
        /// 
        /// 
        /// The stream into which the compound file code writes cleartext bytes.
        ///  
        /// 
        /// This method is used only by the DataSpaceManager, so we declare it as an explicit 
        /// interface implementation to hide it from the public interface. 
        /// 
        Stream 
        IDataTransform.GetTransformedStream(
            Stream encodedStream,
            IDictionary transformContext
            ) 
        {
            // 
            // The compound file code shouldn't be calling us until it's made us ready. 
            //
            Debug.Assert(this.IsReady); 

            //
            // After a stream has been handed out, we can't change settings any more.
            // 
            _fixedSettings = true;
 
            Stream s = new RightsManagementEncryptedStream(encodedStream, _cryptoProvider); 

            // let the versioned stream update our FormatVersion information automatically 
            return new VersionedStream(s, _publishLicenseStream);
        }

        #endregion Interface Implementation Methods 

        //----------------------------------------------------- 
        // 
        // Internal Nested Types
        // 
        //-----------------------------------------------------

        #region Internal Nested Types
 
        /// 
        /// Delegate type used by EnumUseLicenseStreams. 
        ///  
        internal delegate void
        UseLicenseStreamCallback( 
            RightsManagementEncryptionTransform rmet,
            StreamInfo si,
            object param,
            ref bool stop 
            );
 
        #endregion Internal Nested Types 

        //----------------------------------------------------- 
        //
        // Internal Methods
        //
        //------------------------------------------------------ 

        ///  
        /// Enumerate the use license streams in the compound file, invoking a caller- 
        /// supplied delegate for each one.
        ///  
        /// 
        /// The delegate to be invoked for each use license stream.
        /// 
        ///  
        /// A caller-supplied parameter to be passed to the callback. Can be null if
        /// the callback requires no additional information. 
        ///  
        /// 
        /// The use license streams are all the streams in the use license storage 
        /// whose names begin with a certain prefix.
        /// 
        /// 
        /// If  is null. 
        /// 
        ///  
        /// If the RM information in this file cannot be read by the current version of 
        /// this class.
        ///  
        internal void
        EnumUseLicenseStreams(
            UseLicenseStreamCallback callback,
            object param 
            )
        { 
            if (callback == null) 
            {
                throw new ArgumentNullException("callback"); 
            }

            bool stop = false;
 
            foreach (StreamInfo si in _useLicenseStorage.GetStreams())
            { 
                // Stream names: we preserve casing, but do case-insensitive comparison (Native CompoundFile API behavior) 
                if (String.CompareOrdinal(
                                LicenseStreamNamePrefix.ToUpperInvariant(), 0, 
                                si.Name.ToUpperInvariant(), 0,
                                LicenseStreamNamePrefixLength
                                ) == 0)
                { 
                    callback(this, si, param, ref stop);
                    if (stop) 
                    { 
                        break;
                    } 
                }
            }
        }
 
        /// 
        /// Load the use license, and the user to whom it was issued, from the specified 
        /// stream. 
        /// 
        ///  
        /// The UTF 8 BinaryReader from which the use license and user are to be loaded.
        /// 
        /// 
        /// The user specified in the stream. 
        /// 
        ///  
        /// The use license from the stream. 
        /// 
        ///  
        /// This method is internal rather than private because it is used by
        /// UserUseLicenseDictionaryLoader.AddUseLicenseFromStreamToDictionary.
        /// 
        internal UseLicense 
        LoadUseLicenseAndUserFromStream(
            BinaryReader utf8Reader, 
            out ContentUser user 
            )
        { 
            utf8Reader.BaseStream.Seek(0, SeekOrigin.Begin);

            //
            // The stream begins with a header of the following format: 
            //
            //      Int32   headerLength 
            //      Int32   userNameLen 
            //      Byte    userName[userNameLen]
            // 
            // ... and then continues with:
            //
            //      In32    useLicenseLen
            //      Byte    useLicense[useLicenseLen]; 
            //
            Int32 headerLength = utf8Reader.ReadInt32(); 
            if (headerLength < UseLicenseStreamLengthMin) 
            {
                throw new FileFormatException(SR.Get(SRID.UseLicenseStreamCorrupt)); 
            }

            //
            // The type-prefixed user name string (e.g., "windows:domain\alias") was 
            // treated as a sequence of little-Endian UTF-16 characters. The octet
            // sequence representing those characters in that encoding were Base-64 
            // encoded. The resulting character string was then UTF-8 encoded. The 
            // resulting byte-length-prefixed UTF-8 byte sequence is what was stored
            // in the use license stream. 
            //
            string base64UserName = ReadLengthPrefixedString(utf8Reader, Encoding.UTF8, UserNameLengthMax);
            byte[] userNameBytes = Convert.FromBase64String(base64UserName);
 
            string typePrefixedUserName =
                        new string( 
                            _unicodeEncoding.GetChars(userNameBytes) 
                            );
 
            //
            // Create and return the user object specified by the type-prefixed name.
            // If the type-prefixed name is not in a valid format, a FileFormatException
            // will be thrown. 
            //
            AuthenticationType authenticationType; 
            string userName; 
            ParseTypePrefixedUserName(typePrefixedUserName, out authenticationType, out userName);
            user = new ContentUser(userName, authenticationType); 

            //
            // Read the use license as a length-prefixed string, and return it. If the stream
            // is shorter than the length prefix implies, an exception will be thrown. 
            //
            return new UseLicense( 
                            ReadLengthPrefixedString(utf8Reader, Encoding.UTF8, UseLicenseLengthMax) 
                            );
        } 

        /// 
        /// Callback function used by LoadUseLicense. Called once for each use license
        /// stream in the compound file. Extracts the use license for the specified 
        /// user.
        ///  
        ///  
        /// The object that knows how to extract license information from the compound file.
        ///  
        /// 
        /// A stream containing a user/user license pair.
        /// 
        ///  
        /// Caller-supplied parameter to EnumUseLicenseStreams. In this case, it is a
        /// LoadUseLicenseForUserParams object. 
        ///  
        /// 
        /// Set to true if the callback function wants to stop the enumeration. This callback 
        /// function never wants to stop the enumeration, so this parameter is not used.
        /// 
        private void
        LoadUseLicenseForUser( 
            RightsManagementEncryptionTransform rmet,
            StreamInfo si, 
            object param, 
            ref bool stop
            ) 
        {
            LoadUseLicenseForUserParams lulfup = param as LoadUseLicenseForUserParams;
            if (lulfup == null)
            { 
                throw new ArgumentException(SR.Get(SRID.CallbackParameterInvalid), "param");
            } 
 
            ContentUser userDesired = lulfup.User;
            Debug.Assert(userDesired != null); 

            ContentUser userFromStream = null;
            using (Stream stream = si.GetStream(FileMode.Open, FileAccess.Read))
            { 
                using (BinaryReader utf8Reader = new BinaryReader(stream, Encoding.UTF8))
                { 
                    userFromStream = rmet.LoadUserFromStream(utf8Reader); 

                    if (userFromStream.GenericEquals(userDesired)) 
                    {
                        lulfup.UseLicense = rmet.LoadUseLicenseFromStream(utf8Reader);
                        stop = true;
                    } 
                }
            } 
 
        }
 
        /// 
        /// Callback function used by SaveUseLicense. Called once for each use license
        /// stream in the compound file. Deletes the use license for the specified
        /// user. 
        /// 
        ///  
        /// The object that knows how the arrangement of license information in the compound file. 
        /// 
        ///  
        /// A stream containing a User-UseLicense pair.
        /// 
        /// 
        /// Caller-supplied parameter to EnumUseLicenseStreams. In this case, it is a 
        /// ContentUser object.
        ///  
        ///  
        /// Set to true if the callback function wants to stop the enumeration. This callback
        /// function never wants to stop the enumeration; it wants to located and delete 
        /// -all- use license streams for the user specified by ,
        /// even though, if the file was created using our APIs, there will never be
        /// more than one.
        ///  
        private void
        DeleteUseLicenseForUser( 
            RightsManagementEncryptionTransform rmet, 
            StreamInfo si,
            object param, 
            ref bool stop
            )
        {
            ContentUser userToDelete = param as ContentUser; 
            if (userToDelete == null)
            { 
                throw new ArgumentException(SR.Get(SRID.CallbackParameterInvalid), "param"); 
            }
 
            ContentUser userFromStream = null;
            using (Stream stream = si.GetStream(FileMode.Open, FileAccess.Read))
            {
                using (BinaryReader utf8Reader = new BinaryReader(stream, Encoding.UTF8)) 
                {
                    userFromStream = rmet.LoadUserFromStream(utf8Reader); 
                } 
            }
 
            if (userFromStream.GenericEquals(userToDelete))
            {
                si.Delete();
            } 
        }
 
        ///  
        /// Create a use license stream containing the use license for the specified
        /// user. 
        /// 
        /// 
        /// The user whose use license is to be saved.
        ///  
        /// 
        /// The use license for the specified user. 
        ///  
        /// 
        /// SaveUseLicense has removed any existing use licenses for this 
        /// user before calling this internal function.
        /// 
        internal void
        SaveUseLicenseForUser( 
            ContentUser user,
            UseLicense useLicense 
            ) 
        {
            // 
            // Generate a unique name for the use license stream, and create the stream.
            //
            string useLicenseStreamName = MakeUseLicenseStreamName();
 
            StreamInfo si = new StreamInfo(_useLicenseStorage, useLicenseStreamName);
 
            // This guarantees a call to Stream.Dispose, which is equivalent to Stream.Close. 
            using (Stream licenseStream = si.Create())
            { 
                // Create a BinaryWriter on the stream.
                using (BinaryWriter utf8Writer = new BinaryWriter(licenseStream, Encoding.UTF8))
                {
                    // 
                    // Construct a type-prefixed user name of the form
                    // "Passport:[....]_smith@hotmail.com" or "Windows:domain\username", 
                    // depending on the authentication type of the user. 
                    //
                    string typePrefixedUserName = MakeTypePrefixedUserName(user); 

                    //
                    // For compatibility with Office, Base64 encode the type-prefixed user name
                    // for the sake of some minimal obfuscation. The parameters to the 
                    // UnicodeEncoding ctor mean: "UTF-16 little-endian, no byte order mark".
                    // Then convert the Base64 characters to UTF-8 encoding. 
                    // 
                    byte [] userNameBytes = _unicodeEncoding.GetBytes(typePrefixedUserName);
                    string base64UserName = Convert.ToBase64String(userNameBytes); 

                    byte [] utf8Bytes = Encoding.UTF8.GetBytes(base64UserName);
                    Int32 utf8ByteLength = utf8Bytes.Length;
 
                    //
                    // Write out a header preceding the use license. The header is of the form: 
                    //      Int32   headerLength 
                    //      Int32   userNameLength          (in bytes)
                    //      Byte    userName[userNameLength] 
                    //      Byte    paddings
                    Int32 headerLength =
                            checked (
                            2 * CU.Int32Size + 
                            utf8ByteLength +
                            CU.CalculateDWordPadBytesLength(utf8ByteLength)); 
 
                    utf8Writer.Write(headerLength);
                    utf8Writer.Write(utf8ByteLength); 
                    utf8Writer.Write(utf8Bytes, 0, utf8ByteLength);
                    WriteDwordPadding(utf8ByteLength, utf8Writer);

                    // 
                    // Write out the use license itself.
                    // 
                    WriteByteLengthPrefixedDwordPaddedString(useLicense.ToString(), utf8Writer, Encoding.UTF8); 
                }
            } 
        }

        //-----------------------------------------------------
        // 
        // Private Nested Types
        // 
        //------------------------------------------------------ 

        #region Private Nested Types 

        /// 
        /// This structure is passed by LoadUseLicense as the callback parameter to
        /// LoadUseLicenseForUser. LoadUseLicense initializes this structure with the 
        /// user whose use license is desired. LoadUseLicenseForUser sets the UseLicense
        /// property when and if it encounters a use license for the specified user. 
        ///  
        private class LoadUseLicenseForUserParams
        { 
            /// 
            /// Constructor.
            /// 
            ///  
            /// The user whose use license is desired.
            ///  
            internal 
            LoadUseLicenseForUserParams(
                ContentUser user 
                )
            {
                _user = user;
                _useLicense = null; 
            }
 
            ///  
            /// The user whose use license is desired.
            ///  
            internal ContentUser User
            {
                get { return _user; }
            } 

            ///  
            /// The use license for the specified user. 
            /// 
            internal UseLicense UseLicense 
            {
                get { return _useLicense; }
                set { _useLicense = value; }
            } 

            private ContentUser _user; 
            private UseLicense _useLicense; 
        }
 
        #endregion Private Nested Types

        //------------------------------------------------------
        // 
        //  Private Methods
        // 
        //----------------------------------------------------- 

        #region Private Methods 

        /// 
        /// Convert a byte array into a string of printable characters by using each sequence
        /// of 5 bits as an index into a table of printable characters. 
        /// 
        ///  
        /// Array containing the bytes to be base-32 encoded. 
        /// 
        ///  
        /// This function dose NOT produce a proper Base32Encoding since it will NOT produce proper padding.
        /// 
        private static char[]
        Base32EncodeWithoutPadding( 
            byte[] bytes
            ) 
        { 
            int numBytes = bytes.Length;
            int numBits  = checked (numBytes * 8); 
            int numChars = numBits / 5;

            // No need to do checked{} since numChars = numBits / 5 where numBits is int.
            if (numBits % 5 != 0) 
                ++numChars;
 
            char[] chars = new char[numChars]; 

            for (int iChar = 0; iChar < numChars; ++iChar) 
            {
                // Starting bit offset from start of byte array.
                // No need to use checked{} here since iChar cannot be bigger than numChars which cannot be
                //  bigger than (max int / 5) 
                int iBitStart = iChar * 5;
 
                // Index into encoding table. 
                int index = 0;
 
                for (int iBit = iBitStart;
                     iBit - iBitStart < 5 && iBit < numBits;
                     ++iBit)
                { 
                    int iByte = iBit / 8;
                    int iBitWithinByte = iBit % 8; 
 
                    if ((bytes[iByte] & (1 << iBitWithinByte)) != 0)
                        index += (1 << (iBit - iBitStart)); 
                }

                chars[iChar] = Base32EncodingTable[index];
            } 

            return chars; 
        } 

        ///  
        /// Create a unique name for a stream to hold a use license, of the form
        /// "EUL-<Base32-encoded GUID>".
        /// 
        private static string MakeUseLicenseStreamName() 
        {
            return LicenseStreamNamePrefix + 
                       new string(Base32EncodeWithoutPadding(Guid.NewGuid().ToByteArray())); 
        }
 
        /// 
        /// Construct a type-prefixed user name of the form "Passport:[....]_smith@hotmail.com"
        /// or "Windows:domain\username", depending on the authentication type of the User.
        ///  
        /// 
        /// The user whose type-prefixed name is to be constructed. 
        ///  
        private static string
        MakeTypePrefixedUserName( 
            ContentUser user
            )
        {
            // Use 9 since we don't do extra allocation for user.AuthenticationType.ToString() 
            //      to get the accurate length
            StringBuilder userName = new StringBuilder(9 /* for Windows: or Passport: */ + user.Name.Length); 
            userName.Append(user.AuthenticationType.ToString()); 
            userName.Append(':');
            userName.Append(user.Name); 

            return userName.ToString();
        }
 
        /// 
        /// Parse a type-prefixed user name of the form "Passport:[....]_smith@hotmail.com" 
        /// or "Windows:domain\username" into its "authentication type" and "user name" 
        /// components (the parts before and after the colon, respectively).
        ///  
        /// 
        /// The string to be parsed.
        /// 
        ///  
        /// Specifies whether the string represents a Windows or Passport user ID.
        ///  
        ///  
        /// The user's ID.
        ///  
        private static void
        ParseTypePrefixedUserName(
            string typePrefixedUserName,
            out AuthenticationType authenticationType, 
            out string userName
            ) 
        { 
            //
            // We don't actually know the authentication type yet, and we might find that 
            // the type-prefixed user name doesn't even specify a valid authentication
            // type. But we have to assign to authenticationType because it's an out
            // parameter.
            // 
            authenticationType = AuthenticationType.Windows;
 
            int colonIndex = typePrefixedUserName.IndexOf(':'); 
            if (colonIndex < 1 || colonIndex >= typePrefixedUserName.Length - 1)
            { 
                throw new FileFormatException(SR.Get(SRID.InvalidTypePrefixedUserName));
            }

            // No need to use checked{} here since colonIndex cannot be >= to (max int - 1) 
            userName = typePrefixedUserName.Substring(colonIndex + 1);
 
            string authenticationTypeString = typePrefixedUserName.Substring(0, colonIndex); 
            bool validEnum = false;
 
            // user names: case-insensitive comparison
            if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(
                    authenticationTypeString,
                    Enum.GetName(typeof(AuthenticationType), AuthenticationType.Windows))) 
            {
                authenticationType = AuthenticationType.Windows; 
                validEnum = true; 
            }
            else if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals( 
                    authenticationTypeString,
                    Enum.GetName(typeof(AuthenticationType), AuthenticationType.Passport)))
            {
                authenticationType = AuthenticationType.Passport; 
                validEnum = true;
            } 
 
            //
            // Didn't find a matching enumeration constant. 
            //
            if (!validEnum)
            {
                throw new FileFormatException( 
                                SR.Get(
                                    SRID.InvalidAuthenticationTypeString, 
                                    typePrefixedUserName 
                                    )
                                ); 
            }
        }

        ///  
        /// Load the use license from the specified stream.
        ///  
        ///  
        /// The Utf 8 BinaryReader from which the use license is to be loaded.
        ///  
        /// 
        /// The use license from the stream.
        /// 
        ///  
        /// For details of the stream format, see the comments in LoadUseLicenseAndUserFromStream.
        ///  
        private UseLicense 
        LoadUseLicenseFromStream(
            BinaryReader utf8Reader 
            )
        {
            utf8Reader.BaseStream.Seek(0, SeekOrigin.Begin);
 
            Int32 headerLength = utf8Reader.ReadInt32();
            if (headerLength < UseLicenseStreamLengthMin) 
            { 
                throw new FileFormatException(SR.Get(SRID.UseLicenseStreamCorrupt));
            } 

            //
            // Skip over the type-prefixed user name string, because we only want the
            // use license. 
            //
            ReadLengthPrefixedString(utf8Reader, Encoding.UTF8, UserNameLengthMax); 
 
            return new UseLicense(
                            ReadLengthPrefixedString(utf8Reader, Encoding.UTF8, UseLicenseLengthMax) 
                            );
        }

        ///  
        /// Load the user to whom a use license was issued from the specified stream.
        ///  
        ///  
        /// The Utf8 BinaryReader from which the user is to be loaded.
        ///  
        /// 
        /// The user specified in the stream.
        /// 
        ///  
        /// For details of the stream format, see the comments in LoadUseLicenseAndUserFromStream.
        ///  
        private ContentUser 
        LoadUserFromStream(
            BinaryReader utf8Reader 
            )
        {
            utf8Reader.BaseStream.Seek(0, SeekOrigin.Begin);
 
            Int32 headerLength = utf8Reader.ReadInt32();
            if (headerLength < UseLicenseStreamLengthMin) 
            { 
                throw new FileFormatException(SR.Get(SRID.UseLicenseStreamCorrupt));
            } 

            string base64UserName = ReadLengthPrefixedString(utf8Reader, Encoding.UTF8, UserNameLengthMax);
            byte[] userNameBytes = Convert.FromBase64String(base64UserName);
 
            string typePrefixedUserName =
                        new string( 
                            _unicodeEncoding.GetChars(userNameBytes) 
                            );
 
            //
            // Create and return the user object specified by the type-prefixed name.
            // If the type-prefixed name is not in a valid format, a FileFormatException
            // will be thrown. 
            //
            AuthenticationType authenticationType; 
            string userName; 
            ParseTypePrefixedUserName(typePrefixedUserName, out authenticationType, out userName);
            return new ContentUser(userName, authenticationType); 
        }

        /// 
        /// Read a string, encoded according to the specified encoding, and prefixed by the 
        /// length in bytes of the encoded string.
        ///  
        ///  
        /// Binary reader from which the string is read.
        ///  
        /// 
        /// Object that specifies how the string has been encoded.
        /// 
        ///  
        /// The maximum number of characters that the string can contain. This prevents a malformed
        /// file with a huge length prefix from making us allocate all our memory. 
        ///  
        private static string
        ReadLengthPrefixedString( 
            BinaryReader reader,
            Encoding encoding,
            int maxLength
            ) 
        {
            Int32 length = reader.ReadInt32(); 
            if (length > maxLength) 
            {
                throw new FileFormatException(SR.Get(SRID.ExcessiveLengthPrefix, length, maxLength)); 
            }

            byte[] bytes = reader.ReadBytes(length);
            if (bytes.Length != length) 
            {
                throw new FileFormatException(SR.Get(SRID.InvalidStringFormat)); 
            } 

            string s = encoding.GetString(bytes); 

            SkipDwordPadding(bytes.Length, reader);

            return s; 
        }
 
        ///  
        /// Skip past the DWORD padding bytes at the end of a string of the specified length.
        ///  
        /// 
        /// Length in bytes of the string that was read.
        /// 
        ///  
        /// Binary reader from which the string was read.
        ///  
        private static void 
        SkipDwordPadding(
            int length, 
            BinaryReader reader
            )
        {
            int extra = length % CU.Int32Size; 
            if (extra != 0)
            { 
                // No need to use checked{} here since we already made sure that extra is smaller than Int32Size 
                byte[] bytes = reader.ReadBytes(CU.Int32Size - extra);
                if (bytes.Length != CU.Int32Size - extra) 
                {
                    throw new FileFormatException(SR.Get(SRID.InvalidStringFormat));
                }
            } 
        }
 
        ///  
        /// Write out the number of bytes needed to DWORD align a string of the specified
        /// length. Per the file format spec, the bytes must be 0s. 
        /// 
        private static void
        WriteDwordPadding(
            int length, 
            BinaryWriter writer
            ) 
        { 
            int extra = length % CU.Int32Size;
            if (extra != 0) 
            {
                // No need to use checked{} here since we already made sure that extra is smaller than Int32Size
                writer.Write(Padding, 0, CU.Int32Size - extra);
            } 
        }
 
        ///  
        /// Write out a string in the specified encoding, preceded by the length in bytes
        /// of the encoded string. Pad the string with 0s to a DWORD boundary. The padding 
        /// is not included in the length prefix.
        /// 
        private static void
        WriteByteLengthPrefixedDwordPaddedString( 
            string s,
            BinaryWriter writer, 
            Encoding encoding 
            )
        { 
            byte[] bytes = encoding.GetBytes(s);
            Int32 length = bytes.Length;

            writer.Write(length); 

            // 
            // NOTE: If we wrote out the string with "Write(string)", the 
            // writer would precede the output with the UTF-8-encoded array
            // length, which we don't want since we need to write the length 
            // ourselves. Use Encoding class to get the bytes and call "Write(bytes)
            //  to avoid that problem.
            //
            writer.Write(bytes); 

            WriteDwordPadding(length, writer); 
        } 

        #endregion Private Methods 

        //------------------------------------------------------
        //
        //  Private Fields 
        //
        //----------------------------------------------------- 
 
        #region Private Fields
 
        private CryptoProvider _cryptoProvider;
        private PublishLicense _publishLicense;
        private bool _fixedSettings;
 
        //
        // Text encoding object used to read or write other strings used in the file 
        // format. (false = little-endian, false = no byte order mark). 
        //
        // NOTE: It doesn't always matter which encoding object we use. It matters when 
        // we're reading or writing character data, but not when we're writing numeric
        // or byte data.
        //
        private static readonly UnicodeEncoding _unicodeEncoding = new UnicodeEncoding(false, false); 

        // 
        // The stream in which the FormatVersion and publish license is stored. 
        //
        private VersionedStreamOwner _publishLicenseStream; 

        //
        // Uninterpreted bytes from the publish license stream header.
        // 
        private byte[] _publishLicenseHeaderExtraBytes;
 
        // 
        // The storage under which use licenses are stored.
        // 
        private StorageInfo _useLicenseStorage;

        //
        // All use licenses reside in streams whose names begin with this prefix: 
        //
        private const string LicenseStreamNamePrefix = "EUL-"; 
        private static readonly int    LicenseStreamNamePrefixLength = LicenseStreamNamePrefix.Length; 

        // 
        // The RM version information for the current version of this class.
        //
        private const string FeatureName = "Microsoft.Metadata.DRMTransform";
 
        //
        // 
        // Maximum permitted length, in characters, of a publish license. 
        //
        private const int PublishLicenseLengthMax = 1000000; 

        //
        // Maximum permitted length, in characters, of a use license.
        // 
        private const int UseLicenseLengthMax = 1000000;
 
        // 
        // Maximum permitted length, in characters, of a base-64-encoded type-prefixed user name.
        // 
        private const int UserNameLengthMax = 1000;

        //
        // Minimum possible length, in bytes, of the header information in a user license 
        // stream. The format of the header is:
        //      Int32   headerLength                    In bytes 
        //      Int32   userNameLength                  In bytes 
        //      Byte    userName[userNameLength]
        // So the shortest possible header, when userNameLength is 1 byte, is 2 Int32s 
        // plus a Byte.
        //
        private static readonly  int UseLicenseStreamLengthMin = 2 * CU.Int32Size + SizeofByte;
 
        private static readonly VersionPair CurrentFeatureVersion = new VersionPair(1,0);
 
        // 
        // The minimum version number that can read the file format that this version of
        // the software writes. 
        //
        private static readonly VersionPair MinimumReaderVersion = new VersionPair(1, 0);

        // 
        // The minimum version number that can update the file format that this version of
        // the software writes. 
        // 
        private static readonly VersionPair MinimumUpdaterVersion = new VersionPair(1, 0);
 
        //
        // Maximum permitted length of the variable-length header in the publish
        // license stream. This limit is a security issue. Since the first 4 bytes
        // of the header specify the header length, and since we will allocate a 
        // buffer to hold the header contents, we don't want somebody to give us
        // a malformed file that specifies a header of length 2^31 or so. 
        // 
        private const int MaxPublishLicenseHeaderLen = 4096;
 
        private static readonly char[] Base32EncodingTable = {
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            '2', '3', '4', '5', '6', '7', '=' 
            };
 
        // 
        // Used to DWORD-align a stream after writing a string to it:
        // 
        private static readonly byte[] Padding = {0, 0, 0};

        private const int SizeofByte  = 1;
 
        #endregion Private Fields
    } 
} 


// 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