/*++

Copyright (c) 1997 Microsoft Corporation

Module Name:

    blob.c

Abstract:

    Generic blob manipulators for the IIS cryptographic package.

    The following routines are exported by this module:

        IISCryptoReadBlobFromRegistry
        IISCryptoWriteBlobToRegistry
        IISCryptoIsValidBlob
        IISCryptoFreeBlob
        IISCryptoCompareBlobs
        IISCryptoCloneBlobFromRawData
        IISCryptoCreateCleartextBlob
        IcpCreateBlob

Author:

    Keith Moore (keithmo)        02-Dec-1996

Revision History:

--*/


#include "precomp.h"
#pragma hdrstop


//
// Private constants.
//


//
// Private types.
//


//
// Private globals.
//


//
// Private prototypes.
//


//
// Public functions.
//


HRESULT
WINAPI
IISCryptoReadBlobFromRegistry(
    OUT PIIS_CRYPTO_BLOB * ppBlob,
    IN HKEY hRegistryKey,
    IN LPCTSTR pszRegistryValueName
    )

/*++

Routine Description:

    This routine creates a new blob, reading the blob out of the
    registry.

Arguments:

    ppBlob - Receives a pointer to the newly created blob if successful.

    hRegistryKey - An open handle to a registry key.

    pszRegistryValueName - The name of the REG_BINARY registry value
        containing the blob.

Return Value:

    HRESULT - Completion status, 0 if successful, !0 otherwise.

--*/

{

    HRESULT result;
    PIIS_CRYPTO_BLOB blob;
    long status;
    DWORD type;
    long length;

    //
    // Sanity check.
    //

    DBG_ASSERT( IcpGlobals.Initialized );
    DBG_ASSERT( ppBlob != NULL );
    DBG_ASSERT( hRegistryKey != NULL );
    DBG_ASSERT( pszRegistryValueName != NULL );

    //
    // Setup our locals so we know how to cleanup on exit.
    //

    blob = NULL;

    //
    // Determine the size of the blob.
    //

    length = 0;

    status = RegQueryValueEx(
                 hRegistryKey,
                 pszRegistryValueName,
                 NULL,
                 &type,
                 NULL,
                 ( LPDWORD )&length
                 );

    if( status != NO_ERROR ) {
        result = RETURNCODETOHRESULT(status);
        goto fatal;
    }

    //
    // Allocate a new blob.
    //

    blob = IcpAllocMemory( length );

    if( blob == NULL ) {
        result = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
        goto fatal;
    }

    //
    // Read the blob.
    //

    status = RegQueryValueEx(
                 hRegistryKey,
                 pszRegistryValueName,
                 NULL,
                 &type,
                 (LPBYTE)blob,
                 ( LPDWORD )&length
                 );

    if( status != NO_ERROR ) {
        result = RETURNCODETOHRESULT(status);
        goto fatal;
    }

    //
    // Validate the blob.
    //

    if( !IISCryptoIsValidBlob( blob ) ) {
        result = HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
        goto fatal;
    }

    //
    // Success!
    //

    *ppBlob = blob;

    UpdateBlobsCreated();
    return NO_ERROR;

fatal:

    if( blob != NULL ) {
        IcpFreeMemory( blob );
    }

    DBG_ASSERT( FAILED(result) );
    return result;

}   // IISCryptoReadBlobFromRegistry


HRESULT
WINAPI
IISCryptoWriteBlobToRegistry(
    IN PIIS_CRYPTO_BLOB pBlob,
    IN HKEY hRegistryKey,
    IN LPCTSTR pszRegistryValueName
    )

/*++

Routine Description:

    This routine writes the given blob to the given registry location.

Arguments:

    pBlob - A pointer to the blob to write.

    hRegistryKey - An open handle to a registry key.

    pszRegistryValueName - The name of the REG_BINARY registry value
        that will receive the blob.

Return Value:

    HRESULT - Completion status, 0 if successful, !0 otherwise.

--*/

{

    long status;

    //
    // Sanity check.
    //

    DBG_ASSERT( IcpGlobals.Initialized );
    DBG_ASSERT( pBlob != NULL );
    DBG_ASSERT( IISCryptoIsValidBlob( pBlob ) );
    DBG_ASSERT( hRegistryKey != NULL );
    DBG_ASSERT( pszRegistryValueName != NULL );

    //
    // Write the blob.
    //

    status = RegSetValueEx(
                 hRegistryKey,
                 pszRegistryValueName,
                 0,
                 REG_BINARY,
                 (LPBYTE)pBlob,
                 IISCryptoGetBlobLength( pBlob )
                 );

    return RETURNCODETOHRESULT(status);

}   // IISCryptoWriteBlobToRegistry


BOOL
WINAPI
IISCryptoIsValidBlob(
    IN PIIS_CRYPTO_BLOB pBlob
    )

/*++

Routine Description:

    This routine determines if the specified blob is indeed a valid
    blob.

Arguments:

    pBlob - The blob to validate.

Return Value:

    BOOL - TRUE if the blob is valid, FALSE otherwise.

--*/

{

    PIC_BLOB blob;
    BOOL result;

    //
    // Sanity check.
    //

    DBG_ASSERT( IcpGlobals.Initialized );
    DBG_ASSERT( pBlob != NULL );

    //
    // Validate the signature.
    //

    blob = (PIC_BLOB)pBlob;

    switch( blob->Header.BlobSignature ) {

    case KEY_BLOB_SIGNATURE :
    case PUBLIC_KEY_BLOB_SIGNATURE :
    case DATA_BLOB_SIGNATURE :
    case HASH_BLOB_SIGNATURE :
    case CLEARTEXT_BLOB_SIGNATURE :
        result = TRUE;
        break;

    default :
        result = FALSE;
        break;

    }

    if( result &&
        blob->Header.BlobSignature != CLEARTEXT_BLOB_SIGNATURE ) {

        //
        // Validate some of the blob internal structure. Note that we
        // don't validate the internal structure of the cleartext blobs,
        // as they do not conform to the normal IC_BLOB structure.
        //

        if( blob->DataLength == 0 ||
            blob->Header.BlobDataLength !=
                CALC_BLOB_DATA_LENGTH( blob->DataLength, blob->SignatureLength ) ) {

            result = FALSE;

        }

    }

    return result;

}   // IISCryptoIsValidBlob

BOOL
WINAPI
IISCryptoIsValidBlob2(
    IN PIIS_CRYPTO_BLOB pBlob
    )

/*++

Routine Description:

    This routine determines if the specified blob is indeed a valid
    blob.

Arguments:

    pBlob - The blob to validate.

Return Value:

    BOOL - TRUE if the blob is valid, FALSE otherwise.

--*/

{

    PIC_BLOB2 blob;
    BOOL      result;

    //
    // Sanity check.
    //

    DBG_ASSERT( IcpGlobals.Initialized );
    DBG_ASSERT( pBlob != NULL );

    //
    // Validate the signature.
    //

    blob = (PIC_BLOB2)pBlob;

    switch( blob->Header.BlobSignature ) {

    case SALT_BLOB_SIGNATURE :
        result = TRUE;
        break;

    default :
        result = FALSE;
        break;

    }

    if( result ) {

        //
        // Validate some of the blob internal structure. Note that we
        // don't validate the internal structure of the cleartext blobs,
        // as they do not conform to the normal IC_BLOB structure.
        //

        if( blob->DataLength == 0 ||
            blob->Header.BlobDataLength !=
                CALC_BLOB_DATA_LENGTH2( blob->DataLength, blob->SaltLength ) ) {

            result = FALSE;

        }

    }

    return result;

}   // IISCryptoIsValidBlob2


HRESULT
WINAPI
IISCryptoFreeBlob(
    IN PIIS_CRYPTO_BLOB pBlob
    )

/*++

Routine Description:

    This routine frees all resources associated with the given blob.
    After this routine completes, the blob is unusable.

Arguments:

    pBlob - The blob to free.

Return Value:

    HRESULT - Completion status, 0 if successful, !0 otherwise.

--*/

{

    //
    // Sanity check.
    //

    DBG_ASSERT( IcpGlobals.Initialized );
    DBG_ASSERT( pBlob != NULL );
    DBG_ASSERT( IISCryptoIsValidBlob( pBlob ) );

    //
    // Corrupt the structure signature before freeing the blob.
    //

    *(PCHAR)(&pBlob->BlobSignature) = 'X';

    //
    // Free the resources.
    //

    IcpFreeMemory( pBlob );

    //
    // Success!
    //

    UpdateBlobsFreed();
    return NO_ERROR;

}   // IISCryptoFreeBlob

HRESULT
WINAPI
IISCryptoFreeBlob2(
    IN PIIS_CRYPTO_BLOB pBlob
    )

/*++

Routine Description:

    This routine frees all resources associated with the given blob.
    After this routine completes, the blob is unusable.

Arguments:

    pBlob - The blob to free.

Return Value:

    HRESULT - Completion status, 0 if successful, !0 otherwise.

--*/

{

    //
    // Sanity check.
    //

    DBG_ASSERT( IcpGlobals.Initialized );
    DBG_ASSERT( pBlob != NULL );
    DBG_ASSERT( IISCryptoIsValidBlob2( pBlob ) );

    //
    // Corrupt the structure signature before freeing the blob.
    //

    *(PCHAR)(&pBlob->BlobSignature) = 'X';

    //
    // Free the resources.
    //

    IcpFreeMemory( pBlob );

    //
    // Success!
    //

    UpdateBlobsFreed();
    return NO_ERROR;

}   // IISCryptoFreeBlob2


BOOL
WINAPI
IISCryptoCompareBlobs(
    IN PIIS_CRYPTO_BLOB pBlob1,
    IN PIIS_CRYPTO_BLOB pBlob2
    )

/*++

Routine Description:

    This routine compares two blobs to determine if they are identical.

Arguments:

    pBlob1 - Pointer to a blob.

    pBlob2 - Pointer to another blob.

Return Value:

    BOOL - TRUE if the blobs match, FALSE otherwise, or if the blobs
        are invalid.

--*/

{

    //
    // Sanity check.
    //

    DBG_ASSERT( IcpGlobals.Initialized );
    DBG_ASSERT( pBlob1 != NULL );
    DBG_ASSERT( pBlob2 != NULL );
    DBG_ASSERT( IISCryptoIsValidBlob( pBlob1 ) );
    DBG_ASSERT( IISCryptoIsValidBlob( pBlob2 ) );

    //
    // Just do a straight memory compare of the two blobs.
    //

    if( memcmp( pBlob1, pBlob2, sizeof(*pBlob1) ) == 0 ) {
        return TRUE;
    }

    //
    // No match.
    //

    return FALSE;

}   // IISCryptoCompareBlobs


HRESULT
WINAPI
IISCryptoCloneBlobFromRawData(
    OUT PIIS_CRYPTO_BLOB * ppBlob,
    IN PBYTE pRawBlob,
    IN DWORD dwRawBlobLength
    )

/*++

Routine Description:

    This routine makes a copy of a blob from raw data. The raw data
    buffer may be unaligned.

Arguments:

    ppBlob - Receives a pointer to the newly created blob if successful.

    pRawBlob - Pointer to the raw blob data.

    dwRawBlobLength - Length of the raw blob data.

Return Value:

    HRESULT - Completion status, 0 if successful, !0 otherwise.

--*/

{

    PIIS_CRYPTO_BLOB newBlob;
    IIS_CRYPTO_BLOB UNALIGNED *unalignedBlob;
    DWORD blobLength;

    //
    // Sanity check.
    //

    DBG_ASSERT( ppBlob != NULL );
    DBG_ASSERT( pRawBlob != NULL );

    //
    // Allocate space for the new blob.
    //

    unalignedBlob = (IIS_CRYPTO_BLOB UNALIGNED *)pRawBlob;
    blobLength = IISCryptoGetBlobLength( unalignedBlob );

    if( blobLength != dwRawBlobLength ) {
        return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
    }

    newBlob = IcpAllocMemory( blobLength );
    if( newBlob != NULL ) {

        //
        // Clone it. The (PCHAR) casts are necessary to force the compiler
        // to copy BYTE-wise.
        //

        RtlCopyMemory(
            (PCHAR)newBlob,
            (PCHAR)unalignedBlob,
            blobLength
            );

        //
        // Validate its contents.
        //

        if( IISCryptoIsValidBlob( newBlob ) ) {

            *ppBlob = newBlob;

            UpdateBlobsCreated();
            return NO_ERROR;

        }

        IcpFreeMemory( newBlob );
        return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );

    }

    return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );

}   // IISCryptoCloneBlobFromRawData

HRESULT
WINAPI
IISCryptoCloneBlobFromRawData2(
    OUT PIIS_CRYPTO_BLOB * ppBlob,
    IN PBYTE pRawBlob,
    IN DWORD dwRawBlobLength
    )

/*++

Routine Description:

    This routine makes a copy of a blob from raw data. The raw data
    buffer may be unaligned.

Arguments:

    ppBlob - Receives a pointer to the newly created blob if successful.

    pRawBlob - Pointer to the raw blob data.

    dwRawBlobLength - Length of the raw blob data.

Return Value:

    HRESULT - Completion status, 0 if successful, !0 otherwise.

--*/

{

    PIIS_CRYPTO_BLOB newBlob;
    IIS_CRYPTO_BLOB UNALIGNED *unalignedBlob;
    DWORD blobLength;

    //
    // Sanity check.
    //

    DBG_ASSERT( ppBlob != NULL );
    DBG_ASSERT( pRawBlob != NULL );

    //
    // Allocate space for the new blob.
    //

    unalignedBlob = (IIS_CRYPTO_BLOB UNALIGNED *)pRawBlob;
    blobLength = IISCryptoGetBlobLength( unalignedBlob );

    if( blobLength != dwRawBlobLength ) {
        return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
    }

    newBlob = IcpAllocMemory( blobLength );
    if( newBlob != NULL ) {

        //
        // Clone it. The (PCHAR) casts are necessary to force the compiler
        // to copy BYTE-wise.
        //

        RtlCopyMemory(
            (PCHAR)newBlob,
            (PCHAR)unalignedBlob,
            blobLength
            );

        //
        // Validate its contents.
        //

        if( IISCryptoIsValidBlob2( newBlob ) ) {

            *ppBlob = newBlob;

            UpdateBlobsCreated();
            return NO_ERROR;

        }

        IcpFreeMemory( newBlob );
        return HRESULT_FROM_WIN32( ERROR_INVALID_DATA );

    }

    return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );

}   // IISCryptoCloneBlobFromRawData2


HRESULT
WINAPI
IISCryptoCreateCleartextBlob(
    OUT PIIS_CRYPTO_BLOB * ppBlob,
    IN PBYTE pBlobData,
    IN DWORD dwBlobDataLength
    )

/*++

Routine Description:

    This routine creates a cleartext blob containing the specified
    data.

Arguments:

    ppBlob - Receives a pointer to the newly created blob if successful.

    pBlobData - Pointer to the blob data.

    dwBlobDataLength - Length of the blob data.

Return Value:

    HRESULT - Completion status, 0 if successful, !0 otherwise.

--*/

{

    PIIS_CRYPTO_BLOB blob;

    //
    // Sanity check.
    //

    DBG_ASSERT( ppBlob != NULL );
    DBG_ASSERT( pBlobData != NULL );

    //
    // Allocate space for the new blob.
    //

    blob = IcpAllocMemory( dwBlobDataLength + sizeof(*blob) );

    if( blob != NULL ) {

        //
        // Initialize the blob.
        //

        blob->BlobSignature = CLEARTEXT_BLOB_SIGNATURE;
        blob->BlobDataLength = dwBlobDataLength;

        RtlCopyMemory(
            blob + 1,
            pBlobData,
            dwBlobDataLength
            );

        *ppBlob = blob;

        UpdateBlobsCreated();
        return NO_ERROR;

    }

    return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );

}   // IISCryptoCreateCleartextBlob


PIC_BLOB
IcpCreateBlob(
    IN DWORD dwBlobSignature,
    IN DWORD dwDataLength,
    IN DWORD dwSignatureLength OPTIONAL
    )

/*++

Routine Description:

    This routine creates a new blob.

Arguments:

    dwBlobSignature - The structure signature for the new blob.

    dwDataLength - The data length for the blob.

    dwSignatureLength - The length of the digital signature, 0 if
        there is no signature for this blob. This value cannot be
        CLEARTEXT_BLOB_SIGNATURE; cleartext blobs are "special".

Return Value:

    PIC_BLOB - Pointer to the newly created blob if successful,
        NULL otherwise.

--*/

{

    PIC_BLOB blob;
    DWORD blobDataLength;

    //
    // Sanity check.
    //

    DBG_ASSERT( dwBlobSignature == KEY_BLOB_SIGNATURE ||
                dwBlobSignature == PUBLIC_KEY_BLOB_SIGNATURE ||
                dwBlobSignature == DATA_BLOB_SIGNATURE ||
                dwBlobSignature == HASH_BLOB_SIGNATURE );

    //
    // Allocate storage for the blob.
    //

    blobDataLength = CALC_BLOB_DATA_LENGTH( dwDataLength, dwSignatureLength );
    blob = IcpAllocMemory( blobDataLength + sizeof(IIS_CRYPTO_BLOB) );

    if( blob != NULL ) {

        //
        // Initialize the blob.
        //

        blob->Header.BlobSignature = dwBlobSignature;
        blob->Header.BlobDataLength = blobDataLength;

        blob->DataLength = dwDataLength;
        blob->SignatureLength = dwSignatureLength;

    }

    return blob;

}   // IcpCreateBlob

PIC_BLOB2
IcpCreateBlob2(
    IN DWORD dwBlobSignature,
    IN DWORD dwDataLength,
    IN DWORD dwSaltLength OPTIONAL
    )

/*++

Routine Description:

    This routine creates a new blob.

Arguments:

    dwBlobSignature - The structure signature for the new blob.

    dwDataLength - The data length for the blob.

    dwSaltLength - The length of the random salt

Return Value:

    PIC_BLOB2 - Pointer to the newly created blob if successful,
        NULL otherwise.

--*/

{

    PIC_BLOB2 blob;
    DWORD blobDataLength;

    //
    // Sanity check.
    //

    DBG_ASSERT( dwBlobSignature == SALT_BLOB_SIGNATURE );

    //
    // Allocate storage for the blob.
    //

    blobDataLength = CALC_BLOB_DATA_LENGTH( dwDataLength, dwSaltLength );
    blob = IcpAllocMemory( blobDataLength + sizeof(IIS_CRYPTO_BLOB) );

    if( blob != NULL ) {

        //
        // Initialize the blob.
        //

        blob->Header.BlobSignature = dwBlobSignature;
        blob->Header.BlobDataLength = blobDataLength;

        blob->DataLength = dwDataLength;
        blob->SaltLength = dwSaltLength;

    }

    return blob;

}   // IcpCreateBlob2


//
// Private functions.
//