/******************************************************************************\
*       This is a part of the Microsoft Source Code Samples. 
*       Copyright (C) 1996 Microsoft Corporation.
*       All rights reserved. 
*       This source code is only intended as a supplement to 
*       Microsoft Development Tools and/or WinHelp documentation.
*       See these sources for detailed information regarding the 
*       Microsoft samples programs.
\******************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <wincrypt.h>

#ifdef USE_BLOCK_CIPHER
    // defines for RC2 block cipher
    #define ENCRYPT_ALGORITHM	CALG_RC2
    #define ENCRYPT_BLOCK_SIZE	8
#else
    // defines for RC4 stream cipher
    #define ENCRYPT_ALGORITHM	CALG_RC4
    #define ENCRYPT_BLOCK_SIZE	1
#endif

static BOOL DecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);

/*****************************************************************************/
void _cdecl main(int argc, char *argv[])
{
    PCHAR szSource	= NULL;
    PCHAR szDestination = NULL;
    PCHAR szPassword	= NULL;

    // Validate argument count.
    if(argc != 3 && argc != 4) {
	printf("USAGE: decrypt <source file> <dest file> [ <password> ]\n");
	exit(1);
    }

    // Parse arguments.
    szSource	   = argv[1];
    szDestination  = argv[2];
    if(argc == 4) {
	szPassword = argv[3];
    }

    if(!DecryptFile(szSource, szDestination, szPassword)) {
	printf("Error encrypting file!\n");
	exit(1);
    }

    exit(0);
}

/*****************************************************************************/
static BOOL DecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword)
{
    FILE *hSource      = NULL;
    FILE *hDestination = NULL;
    INT eof = 0;

    HCRYPTPROV hProv   = 0;
    HCRYPTKEY hKey     = 0;
    HCRYPTHASH hHash   = 0;

    PBYTE pbKeyBlob = NULL;
    DWORD dwKeyBlobLen;

    PBYTE pbBuffer = NULL;
    DWORD dwBlockLen;
    DWORD dwBufferLen;
    DWORD dwCount;

    BOOL status = FALSE;

    // Open source file.
    if((hSource = fopen(szSource,"rb")) == NULL) {
	printf("Error opening Ciphertext file!\n");
        goto done;
    }

    // Open destination file.
    if((hDestination = fopen(szDestination,"wb")) == NULL) {
	printf("Error opening Plaintext file!\n");
        goto done;
    }

    // Get handle to the default provider.
    if(!CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0)) {
        printf("Error %x during CryptAcquireContext!\n", GetLastError());
        goto done;
    }

    if(szPassword == NULL) {
	// Decrypt the file with the saved session key.

	// Read key blob length from source file and allocate memory.
	fread(&dwKeyBlobLen, sizeof(DWORD), 1, hSource);
	if(ferror(hSource) || feof(hSource)) {
	    printf("Error reading file header!\n");
	    goto done;
	}
	if((pbKeyBlob = malloc(dwKeyBlobLen)) == NULL) {
	    printf("Out of memory or improperly formatted source file!\n");
	    goto done;
	}

	// Read key blob from source file.
	fread(pbKeyBlob, 1, dwKeyBlobLen, hSource);
	if(ferror(hSource) || feof(hSource)) {
	    printf("Error reading file header!\n");
	    goto done;
	}

	// Import key blob into CSP.
	if(!CryptImportKey(hProv, pbKeyBlob, dwKeyBlobLen, 0, 0, &hKey)) {
	    printf("Error %x during CryptImportKey!\n", GetLastError());
	    goto done;
	}
    } else {
	// Decrypt the file with a session key derived from a password.

	// Create a hash object.
	if(!CryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) {
	    printf("Error %x during CryptCreateHash!\n", GetLastError());
	    goto done;
	}

	// Hash in the password data.
	if(!CryptHashData(hHash, szPassword, strlen(szPassword), 0)) {
	    printf("Error %x during CryptHashData!\n", GetLastError());
	    goto done;
	}

	// Derive a session key from the hash object.
	if(!CryptDeriveKey(hProv, ENCRYPT_ALGORITHM, hHash, 0, &hKey)) {
	    printf("Error %x during CryptDeriveKey!\n", GetLastError());
	    goto done;
	}

	// Destroy the hash object.
	CryptDestroyHash(hHash);
	hHash = 0;
    }

    // Determine number of bytes to decrypt at a time. This must be a multiple
    // of ENCRYPT_BLOCK_SIZE.
    dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
    dwBufferLen = dwBlockLen;

    // Allocate memory.
    if((pbBuffer = malloc(dwBufferLen)) == NULL) {
	printf("Out of memory!\n");
	goto done;
    }

    // Decrypt source file and write to destination file.
    do {
	// Read up to 'dwBlockLen' bytes from source file.
	dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
	if(ferror(hSource)) {
	    printf("Error reading Ciphertext!\n");
            goto done;
        }
	eof = feof(hSource);

        // Decrypt data
        if(!CryptDecrypt(hKey, 0, eof, 0, pbBuffer, &dwCount)) {
            printf("Error %x during CryptDecrypt!\n", GetLastError());
            goto done;
        }

        // Write data to destination file.
	fwrite(pbBuffer, 1, dwCount, hDestination);
	if(ferror(hDestination)) {
	    printf("Error writing Plaintext!\n");
            goto done;
        }
    } while(!feof(hSource));

    status = TRUE;

    printf("OK\n");

    done:

    // Close files.
    if(hSource) fclose(hSource);
    if(hDestination) fclose(hDestination);

    // Free memory.
    if(pbKeyBlob) free(pbKeyBlob);
    if(pbBuffer) free(pbBuffer);

    // Destroy session key.
    if(hKey) CryptDestroyKey(hKey);

    // Destroy hash object.
    if(hHash) CryptDestroyHash(hHash);

    // Release provider handle.
    if(hProv) CryptReleaseContext(hProv, 0);

    return(status);
}
