RightsManagementProvider.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / TrustUi / MS / Internal / documents / RightsManagementProvider.cs / 1 / RightsManagementProvider.cs

                            //------------------------------------------------------------------------------ 
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
// 
// 
// Description:
//    RightsManagementProvider. 
// 
// History:
//    06/10/05 - [....] created 
//-----------------------------------------------------------------------------

using System;
using System.Collections.Generic; 
using System.Collections.ObjectModel;
using System.IO; 
using System.IO.Packaging; 
using System.Security;
using System.Security.Permissions; 
using System.Security.RightsManagement;
using System.Windows.TrustUI;

using MS.Internal.Documents.Application; 
using MS.Internal.Permissions;
using MS.Internal.PresentationUI; 
using MS.Internal.WindowsBase; // for SecurityHelper 

using Microsoft.Win32; 

namespace MS.Internal.Documents
{
///  
/// RightsManagementProvider is used to connect DRP to RM APIs
///  
///  
/// This class is a facade for the RM APIs. It is the model between the Manager
/// and the EncryptedPackageEnvelope and System.Security.RightsManagement classes. 
/// 
internal class RightsManagementProvider : IRightsManagementProvider, IDisposable
{
    #region Constructors 
    //-------------------------------------------------------------------------
    //  Constructors 
    //------------------------------------------------------------------------- 

    ///  
    /// A constructor that takes an encrypted package.  This
    /// can be null
    /// <The encrypted package
    ///  
    /// 
    /// Critical 
    ///  1) Sets _encryptedPackage that is marked SecurityCritical. 
    /// 
    [MS.Internal.PresentationUI.FriendAccessAllowed] 
    [SecurityCritical]
    public RightsManagementProvider(EncryptedPackageEnvelope encryptedPackage)
    {
        _encryptedPackageEnvelope = encryptedPackage; 
    }
 
    #endregion Constructors 

    #region IRightsManagementProvider 
    //--------------------------------------------------------------------------
    // IRightsManagementProvider
    //-------------------------------------------------------------------------
 
    /// 
    /// Is the XPS document RM-protected? 
    ///  
    /// 
    /// Critical 
    ///  1) Accesses critical data CurrentPublishLicense
    /// TreatAsSafe
    ///  1) Does not leak critical data
    ///  
    bool IRightsManagementProvider.IsProtected
    { 
        [SecurityCritical, SecurityTreatAsSafe] 
        get
        { 
            InitializeMembers();

            return (CurrentPublishLicense != null);
        } 
    }
 
    ///  
    /// Gets the rights granted in the current use license.
    ///  
    /// 
    /// The caller cannot modify the contents of this license because the
    /// setters on the RightsManagementLicense class are SecurityCritical.
    ///  
    RightsManagementLicense IRightsManagementProvider.CurrentUseLicense
    { 
        get { return _rmUseLicense.Value; } 
    }
 
    /// 
    /// Gets or sets the publish license associated with the current package.
    /// Setting the current publish license invalidates any saved use licenses.
    ///  
    /// 
    /// Critical 
    ///  1) Accessing SecurityCritical data _newPublishLicense 
    ///  2) Sets critical for set _useLicense and _rmUseLicense to known safe
    ///     value null 
    /// 
    PublishLicense IRightsManagementProvider.CurrentPublishLicense
    {
        [SecurityCritical] 
        get
        { 
            return _currentPublishLicense; 
        }
 
        [SecurityCritical]
        set
        {
            _currentPublishLicense = value; 

            // Invalidate the saved use license and grants 
            _useLicense.Value = null; 
            _rmUseLicense.Value = null;
        } 
    }

    /// 
    /// Gets the currently active user. 
    /// 
    ///  
    /// Critical 
    ///  1) Exposes critical data (the currently active user)
    ///  
    RightsManagementUser IRightsManagementProvider.CurrentUser
    {
        [SecurityCritical]
        get { return _user.Value; } 
    }
 
    ///  
    /// Enrolls a new user and sets up the secure environment.
    ///  
    /// The account type to use to initialize the
    /// secure environment
    /// 
    /// Critical 
    ///  1) Accessing GetApplicationManifest which is critical
    ///  2) Asserts for RightsManagementPermission to call 
    ///     SecureEnvironment.Create 
    ///  3) Sets SecurityCriticalDataForSet variable _secureEnvironment
    /// TreatAsSafe 
    ///  1) Data into assert is critical or generated locally.  Data coming out
    ///     of the assert is _secureEnvironment, and all methods on instances
    ///     of SecureEnvironment require assert.
    ///  2) The new value is generated using static method 
    ///     SecureEnvironment.Create using return value GetApplicationManifest() which is
    ///     critical (as well as 2 enums which are determined locally). 
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    void IRightsManagementProvider.InitializeEnvironment(EnrollmentAccountType accountType) 
    {
        InitializeMembers();

        CleanUpSecureEnvironment(); 

        try 
        { 
            AuthenticationType authType;
            UserActivationMode userActMode; 

            switch (accountType)
            {
                case (EnrollmentAccountType.Network): 
                    {
                        authType = AuthenticationType.Windows; 
                        userActMode = UserActivationMode.Permanent; 
                        break;
                    } 
                case (EnrollmentAccountType.Temporary):
                    {
                        authType = AuthenticationType.Windows;
                        userActMode = UserActivationMode.Temporary; 
                        break;
                    } 
                case (EnrollmentAccountType.NET): 
                    {
                        authType = AuthenticationType.Passport; 
                        userActMode = UserActivationMode.Permanent;
                        break;
                    }
                default: 
                    {
                        throw new NotSupportedException(); 
                    } 
            }
 
            _rmPermission.Assert(); // BlessedAssert
            try
            {
                _secureEnvironment.Value = SecureEnvironment.Create( 
                                                                GetApplicationManifest(),
                                                                authType, 
                                                                userActMode); 
            }
            finally 
            {
                RightsManagementPermission.RevertAssert();
            }
        } 
        // If the secure environment initialization fails, or the AccountType is
        // set incorrectly, we need to clean out the SecureEnvironment. 
        catch 
        {
            CleanUpSecureEnvironment(); 

            throw;
        }
 
        Trace.SafeWrite(
            Trace.Rights, 
            "SecureEnvironment was initialized as part of enrollment."); 

        SetUserFromSecureEnvironment(); 
    }
    /// 
    /// Sets up the secure environment for a particular user.
    ///  
    /// The user for whom to set up the environment.
    ///  
    /// Critical 
    ///  1) Accesses GetApplicationManifest which is critical.
    ///  2) Asserts for RightsManagementPermission to call 
    ///     SecureEnvironment.Create.
    ///  3) Sets SecurityCriticalDataForSet _secureEnvironment
    /// 
    [SecurityCritical] 
    void IRightsManagementProvider.InitializeEnvironment(RightsManagementUser user)
    { 
        InitializeMembers(); 
        CleanUpSecureEnvironment();
 
        _rmPermission.Assert(); // BlessedAssert
        try
        {
            _secureEnvironment.Value = SecureEnvironment.Create( 
                                                            GetApplicationManifest(),
                                                            user); 
        } 
        finally
        { 
            RightsManagementPermission.RevertAssert();
        }

        Trace.SafeWriteIf( 
            (_secureEnvironment.Value != null),
            Trace.Rights, 
            "SecureEnvironment was initialized for a specific user."); 

        SetUserFromSecureEnvironment(); 
    }

    /// 
    /// Loads a use license for the user from the package. 
    /// This requires InitializeEnvironment to have been called.
    ///  
    /// Whether or not a use license could be loaded directly from the 
    /// package
    ///  
    /// Critical
    ///  1) Asserts for EnvelopePermissionSet to load a use license from the
    ///     encrypted package envelope
    ///  2) Sets the critical value _useLicense from a UseLicense 
    /// TreatAsSafe
    ///  1) Data into elevation is critical for set. 
    ///  2) The use license is loaded from critical data for set 
    ///     _encryptedPackage and is based on critical data for set _user.
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    bool IRightsManagementProvider.LoadUseLicense()
    {
        if (!IsProtected) 
        {
            throw new InvalidOperationException( 
                SR.Get(SRID.RMProviderExceptionNoPackageToDecrypt)); 
        }
 
        UseLicense useLicense;

        SecurityHelper.EnvelopePermissionSet.Assert(); // BlessedAssert
        try 
        {
            useLicense = _encryptedPackageEnvelope 
                .RightsManagementInformation.LoadUseLicense(_user.Value); 
        }
        finally 
        {
            CodeAccessPermission.RevertAll();
        }
 
        if (useLicense != null)
        { 
            Trace.SafeWrite(Trace.Rights, "Existing use license was found."); 
            _useLicense.Value = useLicense;
        } 

        return (useLicense != null);
    }
 
    /// 
    /// Acquires a use license for the package. 
    /// This requires InitializeEnvironment to have been called. 
    /// 
    /// Whether or not a use license could be acquired 
    /// 
    /// Critical
    ///  1) Asserts for RightsManagementPermission to call AcquireUseLicense
    ///  2) Asserts for RightsManagementPermission to call SaveUseLicense 
    ///  3) Accesses critical data CurrentPublishLicense
    ///  4) Sets the critical data _useLicense from the result from the 
    ///     AcquireUseLicense call 
    ///
    /// TreatAsSafe 
    ///  1) Data into elevation is critical for set or returned from a critical
    ///     property.
    ///  2) Data into elevation is critical for set.
    ///  3) Does not leak the publish license. 
    ///  4) Result is from the RM API and is derived from critical data
    ///  
    [SecurityCritical, SecurityTreatAsSafe] 
    bool IRightsManagementProvider.AcquireUseLicense()
    { 
        if (!IsProtected)
        {
            throw new InvalidOperationException(
                SR.Get(SRID.RMProviderExceptionNoPackageToDecrypt)); 
        }
 
        UseLicense useLicense = null; 
        RightsManagementException rmException = null;
 
        _rmPermission.Assert(); // BlessedAssert
        try
        {
            useLicense = CurrentPublishLicense.AcquireUseLicense(_secureEnvironment.Value); 
        }
        catch(RightsManagementException e) 
        { 
            rmException = e;
        } 
        finally
        {
            RightsManagementPermission.RevertAssert();
        } 

        if (useLicense != null) 
        { 
            Trace.SafeWrite(Trace.Rights, "A new use license was acquired.");
 
            _useLicense.Value = useLicense;
        }
        else
        { 
            Trace.SafeWrite(
                Trace.Rights, 
                "New use license acquisition failed: {0}", 
                rmException.Message);
        } 

        return (useLicense != null);
    }
 
    /// 
    /// Saves the current use license and embeds it in the package. 
    /// This requires a use license to have been acquired. 
    /// 
    ///  
    /// Critical
    ///  1) Asserts for EnvelopePermissionSet in order to save the use license
    ///     to a package passed in as an argument
    ///  
    [SecurityCritical]
    void IRightsManagementProvider.SaveUseLicense(EncryptedPackageEnvelope package) 
    { 
        if (!IsProtected)
        { 
            throw new InvalidOperationException(
                SR.Get(SRID.RMProviderExceptionNoPackageToDecrypt));
        }
 
        if (_useLicense.Value == null)
        { 
            throw new InvalidOperationException( 
                SR.Get(SRID.RMProviderExceptionNoUseLicense));
        } 

        if (AllowLicenseCaching)
        {
            Trace.SafeWrite(Trace.Rights, "Writing Use License to package."); 

            // Attempt to write the acquired license back to the package 
            if (package.FileOpenAccess != FileAccess.Read) 
            {
                SecurityHelper.EnvelopePermissionSet.Assert(); // BlessedAssert 
                try
                {
                    package.RightsManagementInformation.
                        SaveUseLicense(_user.Value, _useLicense.Value); 
                }
                finally 
                { 
                    CodeAccessPermission.RevertAll();
                } 
            }
        }
        else
        { 
            Trace.SafeWrite(Trace.Rights, "NOLICCACHE is set, not writing Use License to package.");
        } 
    } 

    ///  
    /// Binds the use license to the secure environment. This sets the
    /// CurrentUseLicense property to the appropriate value.
    /// This requires the use license to be set beforehand.
    ///  
    /// 
    /// Critical 
    ///  1) Calls critical function GenerateCryptoProvider 
    ///  2) Asserts for RightsManagementPermission to get bound grants from the
    ///     CryptoProvider and set the CryptoProvider in the encrypted package 
    ///  3) Modifies CriticalDataForSet _encryptedPackage
    ///  4) Calls critical function ConvertGrantList
    ///  5) Sets the SecurityCriticalDataForSet object _rmLicense by converting
    ///     the UseLicense and bound grants. 
    ///  6) Accesses critical property CurrentPublishLicense
    ///  7) Asserts for RightsManagementPermission to decrypt the publish 
    ///     license. 
    ///  8) Modifies CriticalDataForSet _unsignedPublishLicense
    /// 
    /// TreatAsSafe
    ///  1) The CryptoProvider is not leaked; it is only saved in
    ///     _encryptedPackage, which is critical for get.
    ///  2) Data into and out of elevation is critical for set or not leaked 
    ///     outside this function. The grants are available indirectly through
    ///     the CurrentUseLicense property, but they are not considered 
    ///     critical in this context. 
    ///  3) The CryptoProvider set in _encryptedPackage is retrieved from
    ///     critical function GenerateCryptoProvider. 
    ///  4) The values passed in to the critical function come from critical
    ///     for set member fields _user and _useLicense and the bound grants
    ///     come from a CryptoProvider which comes from a critical call.  The
    ///     result of the call is a RightsManagementLicense object, from which 
    ///     it is not possible to get any critical information (chiefly the
    ///     user name) without calling critical code. 
    ///  5) The data comes from critical function ConvertGrantList. 
    ///  6) The publish license is not saved or leaked, and the operation is
    ///     performed only if the current user is guaranteed to be the owner 
    ///     of the document.
    ///  7) The publish license is decrypted only if the user is the owner of
    ///     the document.  Also, it is impossible to get any information out of
    ///     the saved unsigned publish license without asserting for the 
    ///     RightsManagementPermission.
    ///  8) The unsigned license is saved only if the user is the owner of the 
    ///     document and is set from a call made under an assert. 
    ///
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    void IRightsManagementProvider.BindUseLicense()
    {
        if (!IsProtected) 
        {
            throw new InvalidOperationException( 
                SR.Get(SRID.RMProviderExceptionNoPackageToDecrypt)); 
        }
 
        if (_useLicense.Value == null)
        {
            throw new InvalidOperationException(
                SR.Get(SRID.RMProviderExceptionNoUseLicense)); 
        }
 
        // If a CryptoProvider is already set in the EncryptedPackageEnvelope, 
        // we can't set it again. As a result we should use the existing one if
        // possible. 

        CryptoProvider cryptoProvider = null;
        ReadOnlyCollection grants = null;
 
        _rmPermission.Assert(); // BlessedAssert
        try 
        { 
            cryptoProvider =
                _encryptedPackageEnvelope.RightsManagementInformation.CryptoProvider; 

            if (cryptoProvider != null)
            {
                grants = cryptoProvider.BoundGrants; 
            }
        } 
        finally 
        {
            RightsManagementPermission.RevertAssert(); 
        }

        // If there is no CryptoProvider set in the envelope, we need to create
        // one and associate it with the envelope now 

        if (cryptoProvider == null) 
        { 
            cryptoProvider = GenerateCryptoProvider();
 
            _rmPermission.Assert(); // BlessedAssert
            try
            {
                grants = cryptoProvider.BoundGrants; 

                _encryptedPackageEnvelope.RightsManagementInformation.CryptoProvider = 
                    cryptoProvider; 
            }
            finally 
            {
                RightsManagementPermission.RevertAssert();
            }
        } 

        // Create our RM license object using the grants retrieved from the 
        // CryptoProvider 

        Invariant.Assert( 
            grants != null,
            "CryptoProvider had no bound grants.");

        _rmUseLicense.Value = ConvertGrantList(_user.Value, grants); 

        // If possible use the CryptoProvider to decrypt the publish license 
 
        if (HasPermission(
            _rmUseLicense.Value, RightsManagementPermissions.AllowOwner)) 
        {
            _rmPermission.Assert(); // BlessedAssert
            try
            { 
                _unsignedPublishLicense.Value =
                    CurrentPublishLicense.DecryptUnsignedPublishLicense( 
                        cryptoProvider); 
            }
            finally 
            {
                RightsManagementPermission.RevertAssert();
            }
 
            Trace.SafeWrite(
                Trace.Rights, "Publish license was decrypted."); 
        } 
    }
 
    /// 
    /// Gets a list of all credentials available to the current user.
    /// 
    /// A list of all available credentials 
    /// 
    /// Critical 
    ///  1) Asserts for RightsManagementPermission to call 
    ///     SecureEnvironment.GetActivatedUsers()
    ///  2) Calls SecurityCritical function CreateUser to create 
    ///     RightsManagementUser objects from RightsManagementUser objects returned from
    ///     the RM APIs.
    /// 
    [SecurityCritical] 
    ReadOnlyCollection IRightsManagementProvider.GetAvailableCredentials()
    { 
        ReadOnlyCollection users; 

        _rmPermission.Assert(); // BlessedAssert 
        try
        {
            users = SecureEnvironment.GetActivatedUsers();
        } 
        finally
        { 
            RightsManagementPermission.RevertAssert(); 
        }
 
        List rmUsers =
            new List(users.Count);

        foreach (ContentUser user in users) 
        {
            rmUsers.Add(RightsManagementUser.CreateUser(user)); 
        } 

        return new ReadOnlyCollection(rmUsers); 
    }

    /// 
    /// Gets the default credentials. 
    /// 
    /// Default credentials 
    ///  
    /// Critical
    ///  1) Calls critical code RightsManagementProvider.GetAvailableCredentials 
    ///     and treating User as critical data.
    ///  2) Asserts for RegistryPermission to read default credentials from
    ///     registry. Registry keys and values to access are private const
    ///     strings. 
    ///  3) Calls SecurityCritical RightsManagementUser.CreateUser function
    ///     with data retrieved from the registry. 
    ///  
    [SecurityCritical]
    RightsManagementUser IRightsManagementProvider.GetDefaultCredentials() 
    {
        RightsManagementUser defaultUser = null;

        string defaultUserName = null; 
        int defaultAccountType = -1;  //invalid account
 
        ReadOnlyCollection users = 
            ((IRightsManagementProvider)this).GetAvailableCredentials();
 
        (new RegistryPermission(
            RegistryPermissionAccess.Read, _registryBaseForXpsViewer))
            .Assert(); // BlessedAssert
        try 
        {
            using (RegistryKey defaultRMKey = Registry.CurrentUser.OpenSubKey( 
                _registryLocationForDefaultUser)) 
            {
                if (defaultRMKey != null) 
                {
                    // Get account name and validate it as a string.
                    object keyValue = defaultRMKey.GetValue(_registryValueNameForAccountName);
                    if ((keyValue != null) && 
                        (defaultRMKey.GetValueKind(_registryValueNameForAccountName) == RegistryValueKind.String))
                    { 
                        defaultUserName = (string)keyValue; 
                    }
                    // Get account type and validate it as an int. 
                    keyValue = defaultRMKey.GetValue(_registryValueNameForAccountType);
                    if ((keyValue != null) &&
                        (defaultRMKey.GetValueKind(_registryValueNameForAccountType) == RegistryValueKind.DWord))
                    { 
                        defaultAccountType = (int)keyValue;
                    } 
                } 
            }
        } 
        finally
        {
            RegistryPermission.RevertAssert();
        } 

        //lets match up the user/type string with an actual avalable user 
        if ((users.Count > 0) && (defaultUserName != null)) 
        {
            RightsManagementUser user = RightsManagementUser.CreateUser( 
                defaultUserName,
                (AuthenticationType)defaultAccountType);

            int index = users.IndexOf(user); 

            //Did we find the new default user in our Available users? 
            if (index != -1) 
            {
                defaultUser = users[index]; 
            }
        }

        if ((defaultUser == null) && (users.Count > 0)) 
        {
            //If we get here then default doesn't match any available users. 
            //Select first user as default. 
            defaultUser = users[0];
        } 

        return defaultUser;
    }
 
    /// 
    /// Sets the default credentials. 
    ///  
    /// 
    /// Critical 
    ///  1) Calls critical code RightsManagementProvider.GetAvailableCredentials
    ///     but data retrieved is only saved locally and not leaked.
    ///  2) Asserts for RegistryPermission to set default credentials in
    ///     registry. Registry keys and values to access are private const 
    ///     strings, but data set in the registry comes from an argument to the
    ///     function. 
    ///  3) Accesses Critical properties on RightsManagementUser objects, but 
    ///     does not leak them; the function only saves them in the registry.
    ///  
    [SecurityCritical]
    void IRightsManagementProvider.SetDefaultCredentials(RightsManagementUser user)
    {
        if (user == null) 
        {
            throw new ArgumentNullException("user"); 
        } 

        //Get AvailableCreds list so we can match new default user. 
        ReadOnlyCollection users =
            ((IRightsManagementProvider)this).GetAvailableCredentials();

        int index = users.IndexOf(user); 

        //Did we find the new default user in our Available users? 
        //If not, doesn't save to registry. 
        if (index != -1)
        { 
            string defaultUserName = users[index].Name;
            int defaultAccountType = (int)users[index].AuthenticationType;

            (new RegistryPermission( 
                RegistryPermissionAccess.AllAccess, _registryBaseForXpsViewer))
                .Assert(); //BlessedAssert 
            try 
            {
                using (RegistryKey defaultRMKey = Registry.CurrentUser.CreateSubKey( 
                    _registryLocationForDefaultUser))
                {
                    if (defaultRMKey != null)
                    { 
                        defaultRMKey.SetValue(
                            _registryValueNameForAccountName, defaultUserName); 
                        defaultRMKey.SetValue( 
                            _registryValueNameForAccountType, defaultAccountType);
                    } 
                }
            }
            finally
            { 
                RegistryPermission.RevertAssert();
            } 
        } 
    }
 
    /// 
    /// Removes user from available credentials.
    /// 
    ///  
    /// Critical
    ///  1) Elevation to remove RM user account. 
    ///  
    [SecurityCritical]
    void IRightsManagementProvider.RemoveCredentials(RightsManagementUser user) 
    {
        if (user == null)
        {
            throw new ArgumentNullException("user"); 
        }
 
        _rmPermission.Assert(); // BlessedAssert 
        try
        { 
            SecureEnvironment.RemoveActivatedUser(user);
        }
        finally
        { 
            RightsManagementPermission.RevertAssert();
        } 
    } 

    ///  
    /// Gets a dictionary of all users and grants stored in the package.
    /// 
    /// All the users and licenses in the package, keyed by User.
    ///  
    /// 
    /// Critical 
    ///  1) Sets SecurityCriticalForSet rights dictionary. 
    ///  2) Returns personally identifiable information in rights dictionary
    ///     (e-mail addresses of all users who have rights to the document). 
    ///  3) Asserts for RightsManagementPermission to get user objects from
    ///     ContentGrant objects.
    ///  4) Asserts for RightsManagementPermission to get the owner of the
    ///     unsigned license. 
    ///  5) Calls SecurityCritical function CreateGrant to create a grant for
    ///     the owner. 
    ///  6) Calls (several times) RightsManagementUser.CreateUser with a 
    ///     ContentUser object as an argument
    ///  7) Calls critical function ConvertGrantList to convert the list of 
    ///     grants retrieved from the unsigned publish license.
    /// TreatAsSafe
    ///  1) Data for rights dictionary comes from either the publish license
    ///     or the unsigned publish license, both of which are critical for 
    ///     set objects.
    ///  2) The personally identifiable information in RightsManagementUser 
    ///     objects can only be accessed from critical code. 
    ///  3) The only critical information retrieved from under this assert is
    ///     the user's e-mail address, which is not accessible as described in 
    ///     #2 above.
    ///  4) The only critical information retrieved from under this assert is
    ///     the owner's e-mail address, which is not accessible as described in
    ///     #2 above. 
    ///  5) The arguments to the CreateGrant function are the user and the
    ///     Owner right. The user being given the Owner grant is the owner user 
    ///     retrieved from the critical for set unsigned license under an 
    ///     assert, so it is OK to create a grant for this user.
    ///  6) The ContentUser objects always come from information loaded from 
    ///     the critical for set unsigned publish license.
    ///  7) All data into the critical function comes from the unsigned publish
    ///     license.  The data retrieved only contains one piece of critical
    ///     information, the user name, and this is can only be accessed by 
    ///     critical code.
    ///  
    [SecurityCritical, SecurityTreatAsSafe] 
    IDictionary
    IRightsManagementProvider.GetAllAccessRights() 
    {
        if (IsProtected &&
            _rightsDictionary.Value == null &&
            _rmUseLicense.Value != null && 
            _rmUseLicense.Value.HasPermission(RightsManagementPermissions.AllowOwner) &&
            _unsignedPublishLicense.Value != null) 
        { 
            UnsignedPublishLicense unsignedLicense = _unsignedPublishLicense.Value;
 
            IDictionary> grantDictionary =
                new Dictionary>();

            // Add all the grants from the unsigned license to the dictionary 
            foreach (ContentGrant grant in GetGrantsFromUnsignedLicense(unsignedLicense))
            { 
                ContentUser contentUser = null; 

                _rmPermission.Assert(); //BlessedAssert 
                try
                {
                    contentUser = grant.User;
                } 
                finally
                { 
                    RightsManagementPermission.RevertAssert(); 
                }
 
                RightsManagementUser user =
                    RightsManagementUser.CreateUser(contentUser);

                if (!grantDictionary.ContainsKey(user)) 
                {
                    grantDictionary[user] = new List(); 
                } 

                grantDictionary[user].Add(grant); 
            }

            ContentUser contentUserOwner = null;
 
            _rmPermission.Assert(); //BlessedAssert
            try 
            { 
                contentUserOwner = unsignedLicense.Owner;
            } 
            finally
            {
                RightsManagementPermission.RevertAssert();
            } 

            RightsManagementUser owner = 
                RightsManagementUser.CreateUser(contentUserOwner); 

            // Add a grant for the owner of the document to the dictionary 
            if (!grantDictionary.ContainsKey(owner))
            {
                grantDictionary[owner] = new List(1);
            } 

            ContentGrant ownerGrant = 
                CreateGrant( 
                    owner,
                    ContentRight.Owner, 
                    DateTime.MinValue,
                    DateTime.MaxValue);

            grantDictionary[owner].Add(ownerGrant); 

            // Convert the grant dictionary to a dictionary of licenses 
            IDictionary dictionary = 
                new Dictionary(
                    grantDictionary.Count); 

            foreach (RightsManagementUser user in grantDictionary.Keys)
            {
                RightsManagementLicense currentLicense = 
                    ConvertGrantList(user, grantDictionary[user]);
 
                dictionary[user] = currentLicense; 
            }
 
            _rightsDictionary.Value =
                (IDictionary)dictionary;
        }
 
        return _rightsDictionary.Value;
    } 
 
    /// 
    /// Decrypt the encrypted package into a metro stream. 
    /// 
    /// 
    /// Critical
    ///  1) Asserts for EnvelopePermissionSet to get a decrypted stream from 
    ///     the encrypted package.
    /// TreatAsSafe 
    ///  1) Data into assert is critical for set. The operation of decrypting 
    ///     the package is critical, but the decrypted stream is not critical.
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    Stream IRightsManagementProvider.DecryptPackage()
    {
        if (_encryptedPackageEnvelope == null) 
        {
            throw new InvalidOperationException( 
                SR.Get(SRID.RMProviderExceptionNoPackageToDecrypt)); 
        }
 
        Stream result;

        Trace.SafeWrite(Trace.Rights, "Decrypting the document.");
 
        SecurityHelper.EnvelopePermissionSet.Assert(); // BlessedAssert
        try 
        { 
            result = _encryptedPackageEnvelope.GetPackageStream();
        } 
        finally
        {
            CodeAccessPermission.RevertAll();
        } 

        return result; 
    } 

    ///  
    /// Create an encrypted package from a stream using the current publish license.
    /// 
    /// The stream to encrypt
    /// An EncryptedPackageEnvelope around the stream. 
    /// 
    /// Critical 
    ///  1) Accesses critical data CurrentPublishLicense 
    ///  2) Calls critical function GenerateCryptoProvider
    ///  3) Asserts for EnvelopePermissionSet to create a new encrypted envelope 
    ///  4) Calls critical method IRightsManagementProvider.SaveUseLicense
    /// TreatAsSafe
    ///  1) Does not leak critical data. The publish license is saved
    ///     (encrypted) in the encrypted package created, but this cannot be 
    ///     accessed without the RightsManagementPermission.
    ///  2) The returned value is only saved locally. 
    ///  3) Data into assert is critical for set or returned from a critical 
    ///     function. Only the Create operation itself is critical; the
    ///     result object is not itself critical, since retrieving any useful 
    ///     information (such as encrypted content or the publish license)
    ///     requires an assert.
    ///  4) The use license is, as expected, saved into the encrypted envelope
    ///     that was just created. 
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    EncryptedPackageEnvelope IRightsManagementProvider.EncryptPackage(Stream ciphered) 
    {
        // If the publish license is to not encrypt, return null 
        if (CurrentPublishLicense == null)
        {
            return null;
        } 

        // If the user doesn't have rights to encrypt data, we also should not 
        // attempt to create an EncryptedPackageEnvelope, since we could never 
        // write to it
 
        RightsManagementLicense currentUseLicense =
            ((IRightsManagementProvider)this).CurrentUseLicense;
        if (currentUseLicense != null &&
            !HasPermission(currentUseLicense, RightsManagementPermissions.AllowEdit)) 
        {
            return null; 
        } 

        CryptoProvider cp = GenerateCryptoProvider(); 
        EncryptedPackageEnvelope result = null;

        SecurityHelper.EnvelopePermissionSet.Assert(); // BlessedAssert
        try 
        {
            result = EncryptedPackageEnvelope.Create( 
                ciphered, 
                CurrentPublishLicense,
                cp); 
        }
        finally
        {
            CodeAccessPermission.RevertAll(); 
        }
 
        // Always save the use license back into any new RM protected document 
        if (result != null)
        { 
            ((IRightsManagementProvider)this).SaveUseLicense(result);
        }

        return result; 
    }
 
    ///  
    /// Generates an unsigned publish license for the package from a collection
    /// of licenses. 
    /// 
    /// 
    /// The list of licenses from which to generate a publish license
    ///  
    /// The optional date until when the publish license will be valid
    ///  
    /// A URI to contact to request additional permissions 
    /// 
    /// Critical 
    ///  1) Sets the SecurityCriticalDataForSet object _rightsDictionary
    ///  2) Asserts for RightsManagementPermission to create a new unsigned
    ///     publish license.  No data goes into this assert, and any access to
    ///     the UnsignedPublishLicense object created requires another assert. 
    ///  3) Asserts for RightsManagementPermission to set properties on the
    ///     unsigned publish license that come from the arguments to this 
    ///     function.  The properties in the arguments are safely critical for 
    ///     set.  All other non-argument data is retrieved from member
    ///     variables of this class that are critical for set. 
    ///  4) Sets the SecurityCriticalDataForSet object _unsignedPublishLicense.
    ///     This is set from information passed in to this function as an
    ///     argument.
    ///  
    [SecurityCritical]
    void IRightsManagementProvider.GenerateUnsignedPublishLicense( 
        IList licenses) 
    {
        if (licenses == null) 
        {
            throw new ArgumentNullException("licenses");
        }
 
        // If the document is already protected, only owners can republish it
        // with different permissions 
        if (IsProtected && !HasPermission( 
            _rmUseLicense.Value, RightsManagementPermissions.AllowOwner))
        { 
            throw new InvalidOperationException(
                SR.Get(SRID.RMProviderExceptionNotOwnerOfDocument));
        }
 
        Trace.SafeWrite(
            Trace.Rights, "Creating a publish license for the document."); 
 
        //
        // Create the unsigned publish license 
        //

        UnsignedPublishLicense unsignedPublishLicense = null;
 
        _rmPermission.Assert(); //BlessedAssert
        try 
        { 
            unsignedPublishLicense = new UnsignedPublishLicense();
        } 
        finally
        {
            RightsManagementPermission.RevertAssert();
        } 

        ICollection licenseGrants = 
            GetGrantsFromUnsignedLicense(unsignedPublishLicense); 

        // 
        // Retrieve the referral information from the first license
        //

        string referralInfoName = string.Empty; 
        Uri referralInfoUri = null;
 
        if (licenses.Count > 0) 
        {
            referralInfoName = licenses[0].ReferralInfoName; 
            referralInfoUri = licenses[0].ReferralInfoUri;
        }

        // 
        // Generate grants from the licenses passed in
        // 
 
        Dictionary rights =
            new Dictionary(); 

        // Look through each license passed in and generate grants from it
        foreach (RightsManagementLicense rmLicense in licenses)
        { 
            // If the user has not already been granted rights, add the grants
            // given to the user to the grants dictionary and the grant list 
            if (!rights.ContainsKey(rmLicense.LicensedUser)) 
            {
                rights[rmLicense.LicensedUser] = rmLicense; 

                IList grantList = GetGrantsFromLicense(rmLicense);

                foreach (ContentGrant grant in grantList) 
                {
                    licenseGrants.Add(grant); 
                } 
            }
        } 

        // If the current user was not specified as an owner, add the user to
        // the rights dictionary and grant the user admin privileges forever
        if (!rights.ContainsKey(_user.Value)) 
        {
            ContentGrant ownerGrant = 
                CreateGrant( 
                    _user.Value,
                    ContentRight.Owner, 
                    DateTime.MinValue,
                    DateTime.MaxValue);

            // Add the grant to the unsigned publish license 
            licenseGrants.Add(ownerGrant);
 
            // Add the grant to the rights dictionary 
            IList grantList = new List();
            grantList.Add(ownerGrant); 
            rights[_user.Value] = ConvertGrantList(_user.Value, grantList);
        }

        // 
        // Set up remaining properties of the unsigned publish license
        // 
 
        _rmPermission.Assert(); //BlessedAssert
        try 
        {
            unsignedPublishLicense.Owner = _user.Value;

            unsignedPublishLicense.ReferralInfoName = referralInfoName; 
            unsignedPublishLicense.ReferralInfoUri = referralInfoUri;
        } 
        finally 
        {
            RightsManagementPermission.RevertAssert(); 
        }

        //
        // Save temporary unsigned license and rights dictionary for signing 
        //
 
        _temporaryRightsDictionary.Value = rights; 
        _temporaryUnsignedPublishLicense.Value = unsignedPublishLicense;
    } 

    /// 
    /// Generates an unsigned publish license for the package from
    /// the template. 
    /// 
    ///  
    /// The template from which to generate a publish license 
    /// 
    /// Critical 
    ///  1) Sets the SecurityCriticalDataForSet object _temporaryRightsDictionary
    ///  2) Asserts for RightsManagementPermission to create a new unsigned
    ///     publish license.  The template parameter value is used to create
    ///     the UnsignedPublishLicense, during which the template is validated. 
    ///  3) Sets the SecurityCriticalDataForSet object _temporaryUnsignedPublishLicense.
    ///     This is set from information passed in to this function as an 
    ///     argument. 
    ///  4) Asserts for RightsManagementPermission executes some critical
    ///     methods to check and set the owner and some grants in the document. 
    ///     These are set from critical member variables or from data derived
    ///     from the argument to this function.
    /// 
    [SecurityCritical] 
    void IRightsManagementProvider.GenerateUnsignedPublishLicense(string template)
    { 
        if (string.IsNullOrEmpty(template)) 
        {
            throw new NullReferenceException("template"); 
        }

        UnsignedPublishLicense unsignedPublishLicense = null;
        _rmPermission.Assert(); //BlessedAssert 
        try
        { 
            // Create the license from the template. 
            unsignedPublishLicense = new UnsignedPublishLicense(template);
        } 
        finally
        {
            RightsManagementPermission.RevertAssert();
        } 

        // Get who is currently listed as the owner of the document in the 
        // publish license 
        ContentUser currentOwnerFromLicense = null;
        _rmPermission.Assert(); //BlessedAssert 
        try
        {
            currentOwnerFromLicense = unsignedPublishLicense.Owner;
        } 
        finally
        { 
            RightsManagementPermission.RevertAssert(); 
        }
 
        RightsManagementUser currentOwner = null;

        if (currentOwnerFromLicense != null)
        { 
            currentOwner = RightsManagementUser.CreateUser(
                currentOwnerFromLicense); 
        } 

        // If the listed owner is not the current user, change the listed owner 
        // and ensure that the old owner still maintains owner rights on the
        // document
        if (!_user.Value.Equals(currentOwner))
        { 
            ContentGrant currentOwnerGrant = null;
 
            if (currentOwner != null) 
            {
                currentOwnerGrant = CreateGrant( 
                    currentOwner,
                    ContentRight.Owner,
                    DateTime.MinValue,
                    DateTime.MaxValue); 
            }
 
            _rmPermission.Assert(); //BlessedAssert 
            try
            { 
                unsignedPublishLicense.Owner = _user.Value;

                if (currentOwnerGrant != null)
                { 
                    unsignedPublishLicense.Grants.Add(currentOwnerGrant);
                } 
            } 
            finally
            { 
                RightsManagementPermission.RevertAssert();
            }
        }
 
        // Assign the new publish license.
        _temporaryRightsDictionary.Value = null; 
        _temporaryUnsignedPublishLicense.Value = unsignedPublishLicense; 
    }
 
    /// 
    /// Signs the unsigned publish license and saves a corresponding updated use
    /// license.
    /// This requires GenerateUnsignedPublishLicense to have been called. 
    /// 
    ///  
    /// Critical 
    ///  1) Asserts for UnmanagedCode permission to sign the publish license
    ///  2) Sets the SecurityCriticalData object _newPublishLicense 
    ///  3) Sets the SecurityCriticalDataForSet object _useLicense
    ///  4) Calls critical function CreateGrant
    ///  5) Calls critical function ConvertGrantList
    /// TreatAsSafe 
    ///  1) Unmanaged operation is to sign the unsigned publish license, only if
    ///     the user owns the document or the document is not protected.  Thus 
    ///     the user can never get any more rights than he or she already has. 
    ///     The unsigned publish license is also critical for set.
    ///  2) The new publish license is set from the critical unsigned publish 
    ///     license.
    ///  3) The use license is only set if the user is the owner of the
    ///     document, or the document is unprotected.  It grants the current
    ///     user owner and view rights, which he or she already has to have had 
    ///     to be able to generate a publish license. The unsigned publish
    ///     license from whose signing the use license is generated is also 
    ///     critical for set. 
    ///  4) The grant is created for the critical for set user and gives him or
    ///     her the owner right, which he or she already must have had. 
    ///  5) The arguments to ConvertGrantList are either critical for set or
    ///     come from the Sign operation on the critical for set unsigned
    ///     publish license.  The list of grants contains only the grant
    ///     created by the safe operation described in #4. 
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    void IRightsManagementProvider.SignPublishLicense() 
    {
        // If the document is already protected, only owners can republish it 
        if (IsProtected &&
            !HasPermission(_rmUseLicense.Value,
                           RightsManagementPermissions.AllowOwner))
        { 
            throw new InvalidOperationException(
                SR.Get(SRID.RMProviderExceptionNotOwnerOfDocument)); 
        } 

        UseLicense useLicense; 

        Trace.SafeWrite(
            Trace.Rights, "Signing the publish license for the document.");
 
        _rmPermission.Assert(); // BlessedAssert
        try 
        { 
            CurrentPublishLicense =
                _temporaryUnsignedPublishLicense.Value.Sign( 
                    _secureEnvironment.Value, out useLicense);
        }
        finally
        { 
            RightsManagementPermission.RevertAssert();
        } 
 
        _useLicense.Value = useLicense;
 
        // Copy and clear temporary values
        _unsignedPublishLicense.Value = _temporaryUnsignedPublishLicense.Value;
        _rightsDictionary.Value = _temporaryRightsDictionary.Value;
        _temporaryUnsignedPublishLicense.Value = null; 
        _temporaryRightsDictionary.Value = null;
 
        // If the RightsDictionary exists then set use license. 
        if (_rightsDictionary.Value != null)
        { 
            _rmUseLicense.Value = _rightsDictionary.Value[_user.Value];
        }
        else
        { 
            // Since the RightsDictionary doesn't exist (most likely because we're using
            // a template), generate the owner data. 
            List grantList = new List(); 
            grantList.Add(CreateGrant(_user.Value, ContentRight.Owner, DateTime.MinValue, DateTime.MaxValue));
            _rmUseLicense.Value = ConvertGrantList(_user.Value, grantList); 
        }
    }

    ///  
    /// Saves the current set of licenses.
    ///  
    ///  
    /// Critical
    ///  1) Sets several critical and critical for set variables to their 
    ///     current corresponding values. This is critical, since these values
    ///     should be overwritten only when trusted code requests it.
    /// 
    [SecurityCritical] 
    void IRightsManagementProvider.SaveCurrentLicenses()
    { 
        // Save the current publish license and use licenses for rollback 
        _lastSavedPublishLicense = _currentPublishLicense;
        _lastSavedRMUseLicense.Value = _rmUseLicense.Value; 
        _lastSavedUseLicense.Value = _useLicense.Value;
        _lastSavedRightsDictionary.Value = _rightsDictionary.Value;
    }
 
    /// 
    /// Reverts to the last saved set of licenses. 
    ///  
    /// 
    /// Critical 
    ///  1) Sets several critical and critical for set variables to saved values
    /// 
    [SecurityCritical]
    void IRightsManagementProvider.RevertToSavedLicenses() 
    {
        CurrentPublishLicense = _lastSavedPublishLicense; 
        _useLicense.Value = _lastSavedUseLicense.Value; 
        _rmUseLicense.Value = _lastSavedRMUseLicense.Value;
        _rightsDictionary.Value = _lastSavedRightsDictionary.Value; 

        _lastSavedPublishLicense = null;
        _lastSavedUseLicense.Value = null;
        _lastSavedRMUseLicense.Value = null; 
        _lastSavedRightsDictionary.Value = null;
    } 
 
    /// 
    /// Sets the encapsulated EncryptedPackageEnvelope to a new value and loads 
    /// a new publish license from it.  If the new publish license is the same
    /// as the old one, the function restores the old use license.
    /// 
    /// The new encrypted package 
    /// Whether or not the new encrypted
    /// package has a different publish license 
    ///  
    /// Critical
    ///  1) Setting critical data objects _encryptedPackage, 
    ///     _rmUseLicense, and CurrentPublishLicense, all from the encrypted
    ///     package passed as a parameter
    ///  2) Copies critical data _publishLicenseFromEnvelope, but does not leak
    ///     it outside the method. 
    ///  3) Asserts for RightsManagementPermission to call ToString on the old
    ///     and new publish licenses. The string representations are not leaked. 
    ///  4) Calls Critical function GenerateCryptoProvider, but saves it only 
    ///     in a critical for get member variable.
    ///  5) Asserts for RightsManagementPermission to save the CryptoProvider 
    ///     returned from GenerateCryptoProvider in the encrypted package.
    /// 
    [SecurityCritical]
    void IRightsManagementProvider.SetEncryptedPackage(EncryptedPackageEnvelope newPackage, out bool publishLicenseChanged) 
    {
        PublishLicense savedPublishLicense = null; 
        UseLicense savedUseLicense = null; 
        RightsManagementLicense savedRMLicense = null;
 
        // Save the current set of licenses. In case the publish license in the
        // new encrypted package envelope is the same, these will be restored.
        if (_publishLicenseFromEnvelope != null)
        { 
            savedPublishLicense = _publishLicenseFromEnvelope;
            savedUseLicense = _useLicense.Value; 
            savedRMLicense = _rmUseLicense.Value; 
        }
 
        _encryptedPackageEnvelope = newPackage;
        _publishLicenseFromEnvelope = null;
        CurrentPublishLicense = null;
 
        InitializeMembers();
 
        publishLicenseChanged = true; 

        // If both publish licenses are non-null, compare them 
        if (savedPublishLicense != null &&
            _publishLicenseFromEnvelope != null)
        {
            string serializedSavedPublishLicense = string.Empty; 
            string serializedNewPublishLicense = string.Empty;
 
            _rmPermission.Assert(); //BlessedAssert 
            try
            { 
                serializedSavedPublishLicense = savedPublishLicense.ToString();
                serializedNewPublishLicense = _publishLicenseFromEnvelope.ToString();
            }
            finally 
            {
                RightsManagementPermission.RevertAssert(); 
            } 

            publishLicenseChanged = !string.Equals( 
                serializedSavedPublishLicense,
                serializedNewPublishLicense,
                StringComparison.Ordinal);
        } 
        // If both publish licenses are null, that means the document wasn't
        // protected before and still isn't protected, so the publish license 
        // hasn't changed. 
        else if (savedPublishLicense == null &&
                 _publishLicenseFromEnvelope == null) 
        {
            publishLicenseChanged = false;
        }
 
        if (IsProtected && !publishLicenseChanged)
        { 
            // If the publish license hasn't changed, restore the saved use 
            // license and generate a new CryptoProvider from it.
 
            _useLicense.Value = savedUseLicense;
            _rmUseLicense.Value = savedRMLicense;

            CryptoProvider cryptoProvider = GenerateCryptoProvider(); 

            _rmPermission.Assert(); 
            try 
            {
                _encryptedPackageEnvelope.RightsManagementInformation.CryptoProvider = 
                    cryptoProvider;
            }
            finally
            { 
                RightsManagementPermission.RevertAssert();
            } 
        } 

        // Since the encrypted package envelope has been changed, the last saved 
        // licenses aren't applicable any more.
        _lastSavedPublishLicense = null;
        _lastSavedUseLicense.Value = null;
        _lastSavedRMUseLicense.Value = null; 
        _lastSavedRightsDictionary.Value = null;
 
        Trace.SafeWrite( 
            Trace.Rights,
            "SetEncryptedPackage called. publishLicenseChanged: {0}", 
            publishLicenseChanged);
    }

    #endregion IRightsManagementProvider 

    #region IDisposable Members 
    //-------------------------------------------------------------------------- 
    // IDisposable Members
    //-------------------------------------------------------------------------- 

    /// 
    /// Disposes everything that needs to be disposed.
    ///  
    /// 
    /// Critical 
    ///  1) Accesses critical data _cryptoProviders. 
    ///  2) Asserts for RightsManagementPermission to dispose CryptoProvider
    ///     objects. 
    /// TreatAsSafe
    ///  1) Data from _cryptoProviders is not saved.
    ///  2) Doesn't leak any information from under the assert.
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    public void Dispose() 
    { 
        if (_cryptoProviders != null)
        { 
            foreach (CryptoProvider cryptoProvider in _cryptoProviders)
            {
                _rmPermission.Assert(); //BlessedAssert
                try 
                {
                    cryptoProvider.Dispose(); 
                } 
                finally
                { 
                    RightsManagementPermission.RevertAssert();
                }
            }
 
            _cryptoProviders = null;
        } 
 
        CleanUpSecureEnvironment();
 
        GC.SuppressFinalize(this);
    }

    #endregion IDisposable Members 

    #region Private Methods 
    //------------------------------------------------------------------------- 
    // Private Methods
    //-------------------------------------------------------------------------- 

    /// 
    /// Destroys the RightsManagementProvider.
    ///  
    ~RightsManagementProvider()
    { 
        Dispose(); 
    }
 
    /// 
    /// Binds the current use license to the current secure environment to get
    /// a new CryptoProvider.
    ///  
    /// 
    /// Critical 
    ///  1) Asserts for RightsManagementPermission to get a new CryptoProvider. 
    ///     Data going into the assert comes from critical for set member
    ///     variables. 
    ///  2) Adds the new CryptoProvider to the critical list of CryptoProviders.
    /// 
    [SecurityCritical]
    private CryptoProvider GenerateCryptoProvider() 
    {
        CryptoProvider cryptoProvider = null; 
 
        _rmPermission.Assert(); // BlessedAssert
        try 
        {
            cryptoProvider = _useLicense.Value.Bind(_secureEnvironment.Value);

            Trace.SafeWrite( 
                Trace.Rights, "The CryptoProvider was initialized.");
        } 
        finally 
        {
            RightsManagementPermission.RevertAssert(); 
        }

        // Add a reference to the CryptoProvider to the list of CryptoProviders
        // for disposal 
        if (cryptoProvider != null)
        { 
            if (_cryptoProviders == null) 
            {
                IList cryptoProviders = new List(); 
                _cryptoProviders = cryptoProviders;
            }

            _cryptoProviders.Add(cryptoProvider); 
        }
 
        return cryptoProvider; 
    }
 
    /// 
    /// Checks whether the specified license has the specified permission.
    /// 
    /// The license to check 
    /// The permission for which to check
    /// Whether or not the license has the permission 
    ///  
    /// Critical
    ///  1) Marked Critical to audit changes since security decisions are based 
    ///     on output from this function.
    /// TreatAsSafe
    ///  1) This is only marked critical to audit changes. No unsafe operations
    ///     are performed. 
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    private static bool HasPermission( 
        RightsManagementLicense license, RightsManagementPermissions permission)
    { 
        return license.HasPermission(permission);
    }

    ///  
    /// Adds referral information read from the current publish license to a
    /// use license. 
    ///  
    /// The use license (grant list) to which to add
    /// referral information 
    /// 
    /// Critical
    ///  1) Asserts for RightsManagementPermission to read referral information
    ///     from the publish license 
    ///  2) Sets critical data ReferralInfoName and ReferralInfoUri from the
    ///     publish license 
    /// TreatAsSafe 
    ///  1) Referral information is not leaked - it is critical for get to read
    ///     it from the use license. 
    ///  2) The critical data is set directly from the publish license, which
    ///     is critical for set.
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    private void AddReferralInfo(RightsManagementLicense rmLicense)
    { 
        // If there is no publish license yet there is no information to add 
        if (CurrentPublishLicense == null)
        { 
            return;
        }

        _rmPermission.Assert(); 
        try
        { 
            rmLicense.ReferralInfoName = CurrentPublishLicense.ReferralInfoName; 
            rmLicense.ReferralInfoUri = CurrentPublishLicense.ReferralInfoUri;
        } 
        finally
        {
            RightsManagementPermission.RevertAssert();
        } 
    }
 
    ///  
    /// Converts a collection of grants into a "license" that is usable
    /// internally by this component. 
    /// 
    /// The UseLicense to convert
    /// 
    /// Critical 
    ///  1) Sets critical information in a RightsManagementLicense object.
    ///     This information comes from parameters passed in to this function. 
    ///     (The information about rights granted is instead retrieved through 
    ///     the Critical for set _encryptedPackage.)
    ///  2) Asserts for RightsManagementPermission to get the validity dates 
    ///     from a UseLicense object.  We do not consider this information
    ///     critical data in this context.
    ///  3) Asserts for RightsManagementPermission to get the actual rights
    ///     granted in the grant list.  We do not consider this information 
    ///     critical data in this context.
    ///  
    [SecurityCritical] 
    private RightsManagementLicense ConvertGrantList(
        RightsManagementUser user, 
        IList grantList)
    {
        RightsManagementLicense rmLicense = new RightsManagementLicense();
 
        rmLicense.LicensedUser = user;
        rmLicense.LicensePermissions = RightsManagementPermissions.AllowNothing; 
        rmLicense.ValidFrom = DateTime.MinValue; 
        rmLicense.ValidUntil = DateTime.MaxValue;
 
        AddReferralInfo(rmLicense);

        if (grantList != null)
        { 
            bool canSign = false;
 
            DateTime validFrom = DateTime.MinValue; 
            DateTime validUntil = DateTime.MaxValue;
 
            foreach (ContentGrant grant in grantList)
            {
                ContentRight right;
 
                _rmPermission.Assert(); //BlessedAssert
                try 
                { 
                    right = grant.Right;
 
                    if (grant.ValidFrom > validFrom)
                    {
                        validFrom = grant.ValidFrom;
                    } 

                    if (grant.ValidUntil < validUntil) 
                    { 
                        validUntil = grant.ValidUntil;
                    } 
                }
                finally
                {
                    RightsManagementPermission.RevertAssert(); 
                }
 
                switch (right) 
                {
                    // Translate each grant into a Mongoose permission 

                    case ContentRight.View:
                        rmLicense.AddPermission(
                            RightsManagementPermissions.AllowView); 
                        break;
 
                    case ContentRight.Print: 
                        rmLicense.AddPermission(
                            RightsManagementPermissions.AllowPrint); 
                        break;

                    case ContentRight.Extract:
                        rmLicense.AddPermission( 
                            RightsManagementPermissions.AllowCopy);
                        break; 
 
                    case ContentRight.Owner:
                        rmLicense.AddPermission( 
                            RightsManagementPermissions.AllowOwner);
                        break;

                    // The Edit grant can mean two things: 
                    //
                    //  1) Without any other grants, it means basically nothing 
                    //     since Mongoose doesn't support editing documents. 
                    //  2) In conjunction with the Sign grant, it means that
                    //     the user can sign the document. 
                    //
                    // As a result we have to keep track of the edit and sign
                    // grants separately and determine later whether both were
                    // granted before we can allow the user to sign. 

                    case ContentRight.Edit: 
                        rmLicense.AddPermission( 
                           RightsManagementPermissions.AllowEdit);
                        break; 

                    case ContentRight.Sign:
                        canSign = true;
                        break; 

                    case ContentRight.DocumentEdit: 
                        // DocumentEdit is a custom right, that when applied with 
                        // Edit, we want to treat as our custom right, Sign.
                        canSign = true; 
                        break;
                }
            }
 
            if (rmLicense.HasPermission(RightsManagementPermissions.AllowEdit) &&
                canSign) 
            { 
                rmLicense.AddPermission(
                    RightsManagementPermissions.AllowSign); 
            }

            rmLicense.ValidFrom = validFrom;
            rmLicense.ValidUntil = validUntil; 
        }
 
        return rmLicense; 
    }
 
    /// 
    /// Generates a list of Grant objects corresponding to the data contained
    /// in a RightsManagementLicense object.
    ///  
    /// The RightsManagementLicense to transform
    /// A list of Grant objects 
    ///  
    /// Critical
    ///  1) Calls critical function CreateGrant with information from the 
    ///     license object passed in as an argument.
    /// TreatAsSafe
    ///  1) Permissions granted in the license object are critical for set.
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    private IList GetGrantsFromLicense( 
        RightsManagementLicense rmLicense) 
    {
        RightsManagementUser user = rmLicense.LicensedUser; 

        List grants = new List();

        // Translate each Mongoose permission into a grant 

        if (HasPermission(rmLicense, RightsManagementPermissions.AllowView)) 
        { 
            grants.Add(
                CreateGrant( 
                    user,
                    ContentRight.View,
                    rmLicense.ValidFrom,
                    rmLicense.ValidUntil)); 
        }
 
        if (HasPermission(rmLicense, RightsManagementPermissions.AllowPrint)) 
        {
            grants.Add( 
                CreateGrant(
                    user,
                    ContentRight.Print,
                    rmLicense.ValidFrom, 
                    rmLicense.ValidUntil));
        } 
 
        if (HasPermission(rmLicense, RightsManagementPermissions.AllowCopy))
        { 
            grants.Add(
                CreateGrant(
                    user,
                    ContentRight.Extract, 
                    rmLicense.ValidFrom,
                    rmLicense.ValidUntil)); 
        } 

        bool editRightGranted = false; 

        if (HasPermission(rmLicense, RightsManagementPermissions.AllowEdit))
        {
            grants.Add( 
                CreateGrant(
                    user, 
                    ContentRight.Edit, 
                    rmLicense.ValidFrom,
                    rmLicense.ValidUntil)); 

            editRightGranted = true;
        }
 
        if (HasPermission(rmLicense, RightsManagementPermissions.AllowSign))
        { 
            // The sign permission translates to the combination of the Edit 
            // and Sign grants.  If the user was already granted Edit, there is
            // no need to grant it again. 

            if (!editRightGranted)
            {
                grants.Add( 
                    CreateGrant(
                        user, 
                        ContentRight.Edit, 
                        rmLicense.ValidFrom,
                        rmLicense.ValidUntil)); 
            }

            grants.Add(
                CreateGrant( 
                    user,
                    ContentRight.Sign, 
                    rmLicense.ValidFrom, 
                    rmLicense.ValidUntil));
        } 

        if (HasPermission(rmLicense, RightsManagementPermissions.AllowOwner))
        {
            grants.Add( 
                CreateGrant(
                    user, 
                    ContentRight.Owner, 
                    rmLicense.ValidFrom,
                    rmLicense.ValidUntil)); 
        }

        return grants;
    } 

    ///  
    /// Initializes the necessary member variables.  This can be called more 
    /// than once without ill effect.
    ///  
    /// 
    /// Critical
    ///  1) Asserts for RightsManagementPermission to load a publish license
    ///     from the encrypted package. 
    ///  2) Accesses critical data _publishLicenseFromEnvelope
    ///  3) Sets critical property CurrentPublishLicense 
    ///  4) Accesses _encryptedPackage which is SecurityCritical 
    /// TreatAsSafe
    ///  1) The publish license is saved as critical data, so it is not leaked. 
    ///  2) It is accessed only to check for null and is not null.  It is set
    ///     from critical data _encryptedPackage.
    ///  3) The new publish license is set directly from critical data
    ///     _publishLicenseFromEnvelope. 
    ///  4) Does not leak information.
    ///  
    [SecurityCritical, SecurityTreatAsSafe] 
    private void InitializeMembers()
    { 
        if (_encryptedPackageEnvelope != null &&
            _publishLicenseFromEnvelope == null)
        {
            RightsManagementInformation rmInfo = 
                _encryptedPackageEnvelope.RightsManagementInformation;
            PublishLicense publishLicense = null; 
 
            _rmPermission.Assert(); //BlessedAssert
            try 
            {
                publishLicense = rmInfo.LoadPublishLicense();
            }
            finally 
            {
                RightsManagementPermission.RevertAssert(); 
            } 

            if (publishLicense == null) 
            {
                throw new FileFormatException(
                    SR.Get(SRID.RMProviderExceptionNoPublishLicense));
            } 

            _publishLicenseFromEnvelope = publishLicense; 
            CurrentPublishLicense = publishLicense; 
        }
    } 

    /// 
    /// Cleans up the SecureEnvironment member.
    ///  
    /// 
    /// Critical 
    ///  1) Asserts RightsManagementPermission to call the Dispose method 
    ///     on _secureEnvironment
    ///  2) Sets SecurityCriticalDataForSet object _secureEnvironment 
    /// TreatAsSafe
    ///  1) The assert is made to call Dispose on _secureEnvironment, which
    ///     is marked as SecurityCriticalDataForSet.
    ///  2) The new value being assigned to _secureEnvironment is null. 
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    private void CleanUpSecureEnvironment() 
    {
        if (_secureEnvironment.Value != null) 
        {
            _rmPermission.Assert(); // BlessedAssert
            try
            { 
                _secureEnvironment.Value.Dispose();
            } 
            finally 
            {
                RightsManagementPermission.RevertAssert(); 
            }
            _secureEnvironment.Value = null;
        }
    } 

    ///  
    /// Sets the currently active user from the value stored in the saved 
    /// secure environment.
    ///  
    /// Critical
    ///  1) Asserts for RightsManagementPermission to get the value of the
    ///     _secureEnvironment.Value.User parameter
    ///  2) Sets SecurityCriticalDataForSet variable _user 
    ///  3) Calls SecurityCritical function RightsManagementUser.CreateUser
    /// TreatAsSafe 
    ///  1) _secureEnvironment is SecurityCriticalDataForSet, and the call is 
    ///     reading a property value which requires asserts to access any data
    ///     from it. 
    ///  2) The _user variable is set from SecurityCritical function
    ///     RightsManagementUser.CreateUser.
    ///  3) The argument to the CreateUser function is information that is
    ///     retrieved from the SecureEnvironment created by SecurityCritical 
    ///     method Create.
    [SecurityCritical, SecurityTreatAsSafe] 
    private void SetUserFromSecureEnvironment() 
    {
        _rmPermission.Assert(); // BlessedAssert 
        try
        {
            _user.Value =
                RightsManagementUser.CreateUser(_secureEnvironment.Value.User); 
        }
        finally 
        { 
            RightsManagementPermission.RevertAssert();
        } 
    }

    /// 
    /// Creates a ContentGrant object with the given user and rights. 
    /// 
    /// The user for whom the grant applies 
    /// The right to grant 
    /// A new ContentGrant object
    ///  
    /// Critical
    ///  1) Asserts for RightsManagementPermission to create a ContentGrant
    ///     object with arguments passed in to this function
    ///  
    [SecurityCritical]
    private ContentGrant CreateGrant(RightsManagementUser user, ContentRight right, DateTime validFrom, DateTime validUntil) 
    { 
        _rmPermission.Assert(); //BlessedAssert
        try 
        {
            return new ContentGrant(user, right, validFrom, validUntil);
        }
        finally 
        {
            RightsManagementPermission.RevertAssert(); 
        } 
    }
 
    /// 
    /// Gets a collection of grants from an unsigned publish license.
    /// 
    /// An unsigned publish license 
    /// A collection of grants
    ///  
    /// Critical 
    ///  1) Asserts for RightsManagementPermission to get a list of grants from
    ///     an unsigned publish license. 
    /// TreatAsSafe
    ///  1) Accessing any information in the actual grant objects in the list
    ///     requires an assert for RightsManagementPermission.
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    private ICollection GetGrantsFromUnsignedLicense( 
        UnsignedPublishLicense unsignedLicense) 
    {
        ICollection grants = null; 

        if (unsignedLicense != null)
        {
            _rmPermission.Assert(); //BlessedAssert 
            try
            { 
                grants = unsignedLicense.Grants; 
            }
            finally 
            {
                RightsManagementPermission.RevertAssert();
            }
        } 

        return grants; 
    } 

    ///  
    /// Cleans up the SecureEnvironment member.
    /// 
    /// 
    /// Critical 
    ///   Must assert for SecurityPermission to get path to running executable (XPSViewer.exe unmanaged code)
    ///   Must assert for FileIOPermission to read XPSViewerManifest.xml. 
    ///  
    [SecurityCritical]
    private string GetApplicationManifest() 
    {
        //Content of the RM application Manifest
        System.Diagnostics.ProcessModule processModule = null;
 
        //Get the current Process and MainModule.
        new SecurityPermission(PermissionState.Unrestricted).Assert(); //BlessedAssert 
        try 
        {
            System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess(); 
            processModule = currentProcess.MainModule;
        }
        finally
        { 
            //Revert the SecurityPermission assert
            SecurityPermission.RevertAssert(); 
        } 

        Invariant.Assert( 
            processModule != null,
            "Failed to get Process Module");

        string fileName = processModule.FileName; 
        string applicationManifest = null;
 
        //Using exe module path, create path to application manifest file (XPSViewerManifest.xml) 
        string applicationManifestFileLocation = Path.Combine(  Path.GetDirectoryName(fileName),
                                                                _applicationManifestFileName); 

        new FileIOPermission(FileIOPermissionAccess.Read, applicationManifestFileLocation).Assert(); //BlessedAssert
        try
        { 
            // Create an instance of StreamReader to read from a file.
            // The using statement also closes the StreamReader. 
            using (StreamReader sr = new StreamReader(applicationManifestFileLocation)) 
            {
                applicationManifest = sr.ReadToEnd(); 
            }
        }
        finally
        { 
            FileIOPermission.RevertAssert();
        } 
 
        return applicationManifest;
    } 

    #endregion Private Methods

    #region Private Properties 
    //-------------------------------------------------------------------------
    // Private Properties 
    //------------------------------------------------------------------------- 

    ///  
    /// Gets whether the XPS document is RM protected. This is a convenient way
    /// to use the IsProtected property without having to cast to an instance
    /// of IRightsManagementProvider each time.
    ///  
    private bool IsProtected
    { 
        get 
        {
            return ((IRightsManagementProvider)this).IsProtected; 
        }
    }

    ///  
    /// Gets or sets the current publish license that will be used for saving
    /// an encrypted document.  This is a convenient way to use the 
    /// CurrentPublishLicense property without having to cast to an instance of 
    /// IRightsManagementProvider each time.
    ///  
    /// 
    /// Critical
    ///  1) Calls critical property
    ///     IRightsManagementProvider.CurrentPublishLicense 
    /// 
    private PublishLicense CurrentPublishLicense 
    { 
        [SecurityCritical]
        get 
        {
            return ((IRightsManagementProvider)this).CurrentPublishLicense;
        }
 
        [SecurityCritical]
        set 
        { 
            ((IRightsManagementProvider)this).CurrentPublishLicense = value;
        } 
    }

    /// 
    /// Indicates whether the current Use License allows caching of the license 
    /// back to the container.
    ///  
    /// Critical: 1) Asserts for RightsManagementPermission in order to check the 
    ///              ApplicationData dictionary.
    /// 
    /// TreatAsSafe: 1) The knowledge of whether License Caching is allowed is not
    ///                 confidential information.
    /// 
    ///  
    private bool AllowLicenseCaching
    { 
        [SecurityCritical, SecurityTreatAsSafe] 
        get
        { 
            bool result = false;

            _rmPermission.Assert(); // BlessedAssert
            try 
            {
                // If the key pair "NOLICCACHE","1" (in _noLicCacheKeyValuePair) is present 
                // in the Use License's ApplicationData, this signifies that license 
                // caching should be disabled (and thus we will return false here)
                result = 
                    !(_useLicense.Value != null &&
                    _useLicense.Value.ApplicationData != null &&
                    _useLicense.Value.ApplicationData.Contains(_noLicCacheKeyValuePair));
 
            }
            finally 
            { 
                RightsManagementPermission.RevertAssert();
            } 
            return result;
        }
    }
 
    #endregion Private Properties
 
    #region Private Fields 
    //-------------------------------------------------------------------------
    // Private Fields 
    //--------------------------------------------------------------------------

    /// 
    /// The underlying EncryptedPackageEnvelope class from the RM APIs. 
    /// 
    [SecurityCritical] 
    EncryptedPackageEnvelope _encryptedPackageEnvelope; 

    ///  
    /// The currently active secure environment.
    /// 
    /// 
    /// This information does not need to be critical for get because it is 
    /// not possible to get critical information from the encapsulated object
    /// without asserting for RightsManagementPermission. 
    ///  
    SecurityCriticalDataForSet _secureEnvironment;
 
    /// 
    /// The use license the user has for the currently open package.
    /// 
    ///  
    /// This information does not need to be critical for get because it is
    /// not possible to get critical information from the encapsulated user 
    /// object without asserting for RightsManagementPermission. 
    /// 
    SecurityCriticalDataForSet _useLicense; 

    /// 
    /// The last saved use license.
    ///  
    /// 
    /// This information does not need to be critical for get because it is 
    /// not possible to get critical information from the encapsulated user 
    /// object without asserting for RightsManagementPermission.
    ///  
    SecurityCriticalDataForSet _lastSavedUseLicense;

    /// 
    /// A copy of the unsigned publish license. 
    /// 
    ///  
    /// This information does not need to be critical for get because it is 
    /// not possible to get critical information from the encapsulated user
    /// object without asserting for RightsManagementPermission. 
    /// 
    SecurityCriticalDataForSet _unsignedPublishLicense;

    ///  
    /// A generated unsigned publish license that has not yet been signed. Once
    /// it is signed, it will replace the _unsignedPublishLicense above. 
    ///  
    /// 
    /// This information does not need to be critical for get because it is 
    /// not possible to get critical information from the encapsulated user
    /// object without asserting for RightsManagementPermission.
    /// 
    SecurityCriticalDataForSet _temporaryUnsignedPublishLicense; 

    ///  
    /// The publish license saved in the current _encryptedPackage. 
    /// 
    [SecurityCritical] 
    PublishLicense _publishLicenseFromEnvelope;

    /// 
    /// The current publish license, which may be different from 
    /// _publishLicenseFromEnvelope above if the user has committed a publishing
    /// operation. 
    ///  
    [SecurityCritical]
    PublishLicense _currentPublishLicense; 

    /// 
    /// The last saved publish license.
    ///  
    [SecurityCritical]
    PublishLicense _lastSavedPublishLicense; 
 
    /// 
    /// The specially formatted version of the use license describing what 
    /// rights the current user has on the document.
    /// 
    /// 
    /// This information does not need to be critical for get because it is 
    /// not possible to get critical information from the encapsulated user
    /// object without asserting for RightsManagementPermission. 
    ///  
    SecurityCriticalDataForSet _rmUseLicense;
 
    /// 
    /// The last saved RM use license.
    /// 
    ///  
    /// This information does not need to be critical for get because it is
    /// not possible to get critical information from the encapsulated user 
    /// object without asserting for RightsManagementPermission. 
    /// 
    SecurityCriticalDataForSet _lastSavedRMUseLicense; 

    /// 
    /// The user for whom this document has been opened.
    ///  
    /// 
    /// This information does not need to be critical for get because it is 
    /// not possible to get critical information from a RightsManagementUser 
    /// object outside critical code.
    ///  
    SecurityCriticalDataForSet _user;

    /// 
    /// A dictionary of rights granted to users on this document. 
    /// 
    ///  
    /// This information does not need to be critical for get because it is 
    /// not possible to get critical information from the encapsulated user
    /// objects without asserting for RightsManagementPermission. 
    /// 
    SecurityCriticalDataForSet<
        IDictionary> _rightsDictionary;
 
    /// 
    /// The last saved version of the dictionary of rights granted to users. 
    ///  
    /// 
    /// This information does not need to be critical for get because it is 
    /// not possible to get critical information from the encapsulated user
    /// objects without asserting for RightsManagementPermission.
    /// 
    SecurityCriticalDataForSet< 
        IDictionary> _lastSavedRightsDictionary;
 
    ///  
    /// A dictionary of rights corresponding to the rights granted in a
    /// temporary unsigned publish license. 
    /// 
    /// 
    /// This information does not need to be critical for get because it is
    /// not possible to get critical information from the encapsulated user 
    /// objects without asserting for RightsManagementPermission.
    ///  
    SecurityCriticalDataForSet< 
        IDictionary> _temporaryRightsDictionary;
 
    /// 
    /// A list of all the CryptoProviders generated
    /// 
    ///  
    /// Since CryptoProvider objects can only be generated from under an assert
    /// the data the list contains is critical. 
    ///  
    [SecurityCritical]
    IList _cryptoProviders; 

    //Name of the RM application manifest.
    /// 
    /// Critical 
    ///  1) Data is used in elevation.
    ///  
    [SecurityCritical] 
    private const string _applicationManifestFileName = "XPSViewerManifest.xml";
 
    /// 
    /// Critical
    ///  1) Data is used in elevation.
    /// 
    /// TreatAsSafe
    ///  1) Marked critical only to audit changes. 
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    private const string _registryLocationForDefaultUser = 
        @"Software\Microsoft\XPSViewer\";

    /// 
    /// Critical 
    ///  1) Data is used in elevation.
    /// 
    /// TreatAsSafe 
    ///  1) Marked critical only to audit changes.
    ///  
    [SecurityCritical, SecurityTreatAsSafe]
    private const string _registryBaseForXpsViewer =
        @"HKEY_CURRENT_USER\Software\Microsoft\XPSViewer\";
 
    /// 
    /// Critical 
    ///  1) Data is used in elevation. 
    ///
    /// TreatAsSafe 
    ///  1) Marked critical only to audit changes.
    /// 
    [SecurityCritical, SecurityTreatAsSafe]
    private const string _registryValueNameForAccountName = "AccountName"; 

    ///  
    /// Critical 
    ///  1) Data is used in elevation.
    /// 
    /// TreatAsSafe
    ///  1) Marked critical only to audit changes.
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    private const string _registryValueNameForAccountType = "AccountType";
 
    ///  
    /// The Key/Value pair for the NOLICCACHE publishing option.  This disables
    /// caching of the Use License in the container if present. 
    /// 
    /// Critical
    ///  1) Data is used to determine which Use License property to check,
    ///     we want to ensure this is not maliciously used to read properties 
    ///     other than NOLICCACHE.
    ///  
    ///  
    [SecurityCritical]
    private readonly KeyValuePair _noLicCacheKeyValuePair = 
        new KeyValuePair("NOLICCACHE", "1");

    /// 
    /// Critical 
    ///  1) Creation of the permission is critical as the code will be
    ///     asserting for it and we do not want it replaced without review. 
    /// TreatAsSafe 
    ///  1) Future reviewers, this value should only ever represent
    ///     RightsManagementPermission.  It should not be changed without 
    ///     reviewing all uses of it in the class. (This is safe because it�s
    ///     being reviewed.)
    /// 
    [SecurityCritical, SecurityTreatAsSafe] 
    private static RightsManagementPermission _rmPermission =
        new RightsManagementPermission(); 
 
    #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