// AuxContext.cpp -- Auxiliary Provider Context wrapper functor to
// manage allocation of a temporal context to one of the Microsoft
// CSPs (for use as a supplemental CSP).

// (c) Copyright Schlumberger Technology Corp., unpublished work, created
// 1999. This computer program includes Confidential, Proprietary
// Information and is a Trade Secret of Schlumberger Technology Corp. All
// use, disclosure, and/or reproduction is prohibited unless authorized
// in writing.  All Rights Reserved.

#include "stdafx.h"
#include <string>

#include <malloc.h>                               // for _alloca

#include <scuOsExc.h>

#include "AuxContext.h"
#include "Uuid.h"

using namespace std;

///////////////////////////    HELPER     /////////////////////////////////

///////////////////////////    PUBLIC     /////////////////////////////////

                                                  // Types
                                                  // C'tors/D'tors
AuxContext::AuxContext()
    : m_hcryptprov(0),
      m_fDeleteOnDestruct(false),
      m_szProvider()
{
    // Acquire a context to a "temporal" container to one of the
    // Microsoft CSPs for use as an auxiliary CSP.  Attempt is first
    // made for the strong crypto provider (MS Enhanced CSP).  If that
    // isn't available (installed), then an attempt is made for the MS
    // Base CSP.

    // The existence of any objects stored in the acquired container
    // is only for the life of this context object (temporal).  This
    // is implemented by using a feature (as yet undocumented) that
    // was added to the Microsoft CSPs to support the notion of
    // "temporal" or "memory resident" container.  Temporal containers
    // are created by acquiring context with a NULL/empty container
    // name using the CRYPT_VERIFYCONTEXT flag.  These are containers
    // whose associated contents (keys, hashes, etc.) are deleted when
    // the last context to that container is released.  Temporal
    // containers are preferred over creating and releasing/deleting
    // containers with temporary names so the resources used will be
    // freed if the application exists abnormally and not pollute the
    // container name space.

    // COMPATIBILITY ISSUE: Since temporal containers weren't
    // supported by the MS CSP until Windows 2000 Beta 2 (Build 1840),
    // a few hurdles are overcome to acheive similar functionality
    // using previous versions.  It's unclear when temporal containers
    // will be supported on W95/98 & NT 4.  As a result, two methods
    // of acquiring a context to the auxiliary CSP is used.

    // For environments that don't support temporal containers, a
    // normal context is acquired to a uniquely named container since
    // the default container may be used by other
    // applications/threads.  The CRYPT_VERIFYCONTEXT flag can not be
    // used since keys may want to be imported to the temporal
    // container and this characteristic isn't support until Windows 2000.
    // Upon destruction of the object, the container is deleted along
    // with any of its contents just as a first class temporal
    // container.

    static LPCTSTR const aszCandidateProviders[] = {
        MS_ENHANCED_PROV,
        MS_DEF_PROV
    };

    OSVERSIONINFO osVer;
    ZeroMemory(&osVer, sizeof osVer);
    osVer.dwOSVersionInfoSize = sizeof osVer;

    if (!GetVersionEx(&osVer))
        throw scu::OsException(GetLastError());

    basic_string<unsigned char> sContainerName;
    DWORD dwAcquisitionFlags;
    if ((VER_PLATFORM_WIN32_WINDOWS == osVer.dwPlatformId) ||
        ((VER_PLATFORM_WIN32_NT == osVer.dwPlatformId) &&
         (5 > osVer.dwMajorVersion)))
    {
        m_fDeleteOnDestruct = true;

        // Construct a container name that is unique for this thread
        static char unsigned const szRootContainerName[] = "SLBCSP-";
        sContainerName = szRootContainerName;     // prefix for easy debugging
        sContainerName.append(Uuid().AsUString());

        dwAcquisitionFlags = CRYPT_NEWKEYSET;
    }
    else
    {
        m_fDeleteOnDestruct = false;
        dwAcquisitionFlags = CRYPT_VERIFYCONTEXT;
    }

    bool fCandidateFound = false;
    for (size_t i = 0;
         (i < (sizeof aszCandidateProviders /
               sizeof *aszCandidateProviders) && !fCandidateFound); i++)
    {
		CString csCntrName(sContainerName.c_str());
        if (CryptAcquireContext(&m_hcryptprov,
                                (LPCTSTR)csCntrName,
                                aszCandidateProviders[i],
                                PROV_RSA_FULL, dwAcquisitionFlags))
        {
            fCandidateFound = true;
            m_szProvider = aszCandidateProviders[i];
        }
    }

    if (!fCandidateFound)
        throw scu::OsException(GetLastError());
}

AuxContext::AuxContext(HCRYPTPROV hcryptprov,
                       bool fTransferOwnership)
    : m_hcryptprov(hcryptprov),
      m_fDeleteOnDestruct(fTransferOwnership),
      m_szProvider()
{}

AuxContext::~AuxContext()
{
    if (0 != m_hcryptprov)
    {
        if (m_fDeleteOnDestruct)
        {
            char *pszContainerName = 0;
            DWORD dwNameLength;
            if (CryptGetProvParam(m_hcryptprov, PP_CONTAINER, NULL,
                                  &dwNameLength, 0))
            {
                pszContainerName =
                    static_cast<char *>(_alloca(dwNameLength));
                if (!CryptGetProvParam(m_hcryptprov, PP_CONTAINER,
                                       reinterpret_cast<char unsigned *>(pszContainerName),
                                       &dwNameLength, 0))
                    pszContainerName = 0;
            }

            if (CryptReleaseContext(m_hcryptprov, 0))
            {
                if (pszContainerName)
                    CryptAcquireContext(&m_hcryptprov, (LPCTSTR)pszContainerName,
                                        m_szProvider, PROV_RSA_FULL,
                                        CRYPT_DELETEKEYSET);
            }
        }
        else    // Just release the context
        {
            CryptReleaseContext(m_hcryptprov, 0);
        }
    }
}



                                                  // Operators
HCRYPTPROV
AuxContext::operator()() const
{
    return m_hcryptprov;
}

                                                  // Operations
                                                  // Access
                                                  // Predicates
                                                  // Static Variables

///////////////////////////   PROTECTED   /////////////////////////////////

                                                  // C'tors/D'tors
                                                  // Operators
                                                  // Operations
                                                  // Access
                                                  // Predicates
                                                  // Static Variables


///////////////////////////    PRIVATE    /////////////////////////////////

                                                  // C'tors/D'tors
                                                  // Operators
                                                  // Operations
                                                  // Access
                                                  // Predicates
                                                  // Static Variables