windows-nt/Source/XPSP1/NT/ds/win32/ntcrypto/fips/driver/algself.c
2020-09-26 16:20:57 +08:00

421 lines
13 KiB
C

/////////////////////////////////////////////////////////////////////////////
// FILE : fipslib.c //
// DESCRIPTION : FIPS 140 support code. //
// AUTHOR : //
// HISTORY : //
// Oct 20 1999 jeffspel/ramas Merge STT into default CSP //
// //
// Copyright (C) 2000 Microsoft Corporation All Rights Reserved //
/////////////////////////////////////////////////////////////////////////////
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <wincrypt.h>
#ifdef KERNEL_MODE
#if WINVER == 0x0500
#include <ntos.h>
#else
#include <ntosp.h>
#endif
#endif
#include <sha.h>
#include <des.h>
#include <tripldes.h>
#include <modes.h>
// known result of an SHA-1 hash on the above buffer
static UCHAR rgbKnownSHA1[] =
{
0xe8, 0x96, 0x82, 0x85, 0xeb, 0xae, 0x01, 0x14,
0x73, 0xf9, 0x08, 0x45, 0xc0, 0x6a, 0x6d, 0x3e,
0x69, 0x80, 0x6a, 0x0c
};
// IV for all block ciphers
UCHAR rgbIV[] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};
// known key, plaintext, and ciphertext for DES
UCHAR rgbDESKey[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
UCHAR rgbDESKnownPlaintext[] = {0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74};
UCHAR rgbDESKnownCiphertext[] = {0x3F, 0xA4, 0x0E, 0x8A, 0x98, 0x4D, 0x48, 0x15};
UCHAR rgbDESCBCCiphertext[] = {0xE5, 0xC7, 0xCD, 0xDE, 0x87, 0x2B, 0xF2, 0x7C};
// known key, plaintext, and ciphertext for 3 key 3DES
UCHAR rgb3DESKey[] =
{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01,
0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23
};
UCHAR rgb3DESKnownPlaintext[] = {0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74};
UCHAR rgb3DESKnownCiphertext[] = {0x31, 0x4F, 0x83, 0x27, 0xFA, 0x7A, 0x09, 0xA8};
UCHAR rgb3DESCBCCiphertext[] = {0xf3, 0xc0, 0xff, 0x02, 0x6c, 0x02, 0x30, 0x89};
// known key, plaintext, and ciphertext for 2 key 3DES
UCHAR rgb3DES112Key[] =
{
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01
};
UCHAR rgb3DES112KnownPlaintext[] = {0x4E, 0x6F, 0x77, 0x20, 0x69, 0x73, 0x20, 0x74};
UCHAR rgb3DES112KnownCiphertext[] = {0xb7, 0x83, 0x57, 0x79, 0xee, 0x26, 0xac, 0xb7};
UCHAR rgb3DES112CBCCiphertext[] = {0x13, 0x4b, 0x98, 0xf8, 0xee, 0xb3, 0xf6, 0x07};
#define MAX_BLOCKLEN 8
#define MAXKEYSTRUCTSIZE DES3_TABLESIZE // currently the max is a 3DES key structure
// Known answer test for SHA HMAC from Fips draft
UCHAR rgbHmacKey [] = {
0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0a,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11, 0x12, 0x13, 0x14,
0x15, 0x16, 0x17, 0x18, 0x19
};
UCHAR rgbHmacData [50]; // set bytes to 0xcd
UCHAR rgbHmac [] = {
0x4c, 0x90, 0x07, 0xf4, 0x02,
0x62, 0x50, 0xc6, 0xbc, 0x84,
0x14, 0xf9, 0xbf, 0x50, 0xc8,
0x6c, 0x2d, 0x72, 0x35, 0xda
};
extern VOID FipsHmacSHAInit(
OUT A_SHA_CTX *pShaCtx,
IN UCHAR *pKey,
IN unsigned int cbKey
);
extern VOID FipsHmacSHAUpdate(
IN OUT A_SHA_CTX *pShaCtx,
IN UCHAR *pb,
IN unsigned int cb
);
extern VOID FipsHmacSHAFinal(
IN A_SHA_CTX *pShaCtx,
IN UCHAR *pKey,
IN unsigned int cbKey,
OUT UCHAR *pHash
);
//
// Function : TestSHA1
//
// Description : This function hashes the passed in message with the SHA1 hash
// algorithm and returns the resulting hash value.
//
void TestSHA1(
UCHAR *pbMsg,
ULONG cbMsg,
UCHAR *pbHash
)
{
A_SHA_CTX HashContext;
// Initialize SHA
A_SHAInit(&HashContext);
// Compute SHA
A_SHAUpdate(&HashContext, pbMsg, cbMsg);
A_SHAFinal(&HashContext, pbHash);
}
//
// Function : TestEncDec
//
// Description : This function expands the passed in key buffer for the appropriate
// algorithm, and then either encryption or decryption is performed.
// A comparison is then made to see if the ciphertext or plaintext
// matches the expected value.
// The function only uses ECB mode for block ciphers and the plaintext
// buffer must be the same length as the ciphertext buffer. The length
// of the plaintext must be either the block length of the cipher if it
// is a block cipher or less than MAX_BLOCKLEN if a stream cipher is
// being used.
//
NTSTATUS TestEncDec(
IN ALG_ID Algid,
IN UCHAR *pbKey,
IN ULONG cbKey,
IN UCHAR *pbPlaintext,
IN ULONG cbPlaintext,
IN UCHAR *pbCiphertext,
IN UCHAR *pbIV,
IN int iOperation
)
{
UCHAR rgbExpandedKey[MAXKEYSTRUCTSIZE];
UCHAR rgbBuffIn[MAX_BLOCKLEN];
UCHAR rgbBuffOut[MAX_BLOCKLEN];
ULONG i;
NTSTATUS Status = STATUS_UNSUCCESSFUL;
RtlZeroMemory(rgbExpandedKey, sizeof(rgbExpandedKey));
RtlZeroMemory(rgbBuffIn, sizeof(rgbBuffIn));
RtlZeroMemory(rgbBuffOut, sizeof(rgbBuffOut));
// length of data to encrypt must be < MAX_BLOCKLEN
if (cbPlaintext > MAX_BLOCKLEN)
{
goto Ret;
}
// alloc for and expand the key
switch(Algid)
{
case (CALG_DES):
{
desparityonkey(pbKey, cbKey);
deskey((DESTable*)rgbExpandedKey, pbKey);
break;
}
case (CALG_3DES):
{
desparityonkey(pbKey, cbKey);
tripledes3key((PDES3TABLE)rgbExpandedKey, pbKey);
break;
}
case (CALG_3DES_112):
{
desparityonkey(pbKey, cbKey);
tripledes2key((PDES3TABLE)rgbExpandedKey, pbKey);
break;
}
}
// if encrypting and there is an IV then use it
if (ENCRYPT == iOperation)
{
memcpy(rgbBuffIn, pbPlaintext, cbPlaintext);
if (NULL != pbIV)
{
for(i = 0; i < cbPlaintext; i++)
{
rgbBuffIn[i] = rgbBuffIn[i] ^ pbIV[i];
}
}
}
// encrypt the plaintext
switch(Algid)
{
case (CALG_DES):
{
if (ENCRYPT == iOperation)
{
des(rgbBuffOut, rgbBuffIn, rgbExpandedKey, ENCRYPT);
}
else
{
des(rgbBuffOut, pbCiphertext, rgbExpandedKey, DECRYPT);
}
break;
}
case (CALG_3DES):
case (CALG_3DES_112):
{
if (ENCRYPT == iOperation)
{
tripledes(rgbBuffOut, rgbBuffIn, rgbExpandedKey, ENCRYPT);
}
else
{
tripledes(rgbBuffOut, pbCiphertext, rgbExpandedKey, DECRYPT);
}
break;
}
}
// compare the encrypted plaintext with the passed in ciphertext
if (ENCRYPT == iOperation)
{
if (memcmp(pbCiphertext, rgbBuffOut, cbPlaintext))
{
goto Ret;
}
}
// compare the decrypted ciphertext with the passed in plaintext
else
{
// if there is an IV then use it
if (NULL != pbIV)
{
for(i = 0; i < cbPlaintext; i++)
{
rgbBuffOut[i] = rgbBuffOut[i] ^ pbIV[i];
}
}
if (memcmp(pbPlaintext, rgbBuffOut, cbPlaintext))
{
goto Ret;
}
}
Status = STATUS_SUCCESS;
Ret:
return Status;
}
//
// Function : TestSymmetricAlgorithm
//
// Description : This function expands the passed in key buffer for the appropriate algorithm,
// encrypts the plaintext buffer with the same algorithm and key, and the
// compares the passed in expected ciphertext with the calculated ciphertext
// to make sure they are the same. The opposite is then done with decryption.
// The function only uses ECB mode for block ciphers and the plaintext
// buffer must be the same length as the ciphertext buffer. The length
// of the plaintext must be either the block length of the cipher if it
// is a block cipher or less than MAX_BLOCKLEN if a stream cipher is
// being used.
//
NTSTATUS TestSymmetricAlgorithm(
IN ALG_ID Algid,
IN UCHAR *pbKey,
IN ULONG cbKey,
IN UCHAR *pbPlaintext,
IN ULONG cbPlaintext,
IN UCHAR *pbCiphertext,
IN UCHAR *pbIV
)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
Status = TestEncDec(Algid, pbKey, cbKey, pbPlaintext, cbPlaintext,
pbCiphertext, pbIV, ENCRYPT);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
Status = TestEncDec(Algid, pbKey, cbKey, pbPlaintext, cbPlaintext,
pbCiphertext, pbIV, DECRYPT);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
Ret:
return Status;
}
// **********************************************************************
// AlgorithmCheck performs known answer tests using the algorithms
// supported by the provider.
// **********************************************************************
NTSTATUS AlgorithmCheck()
{
UCHAR rgbSHA1[A_SHA_DIGEST_LEN];
NTSTATUS Status = STATUS_UNSUCCESSFUL;
A_SHA_CTX ShaCtx;
ULONG ul;
RtlZeroMemory(rgbSHA1, sizeof(rgbSHA1));
// known answer test with SHA-1 (this function is found in hash.c)
TestSHA1("HashThis", 8, rgbSHA1);
if (!RtlEqualMemory(rgbSHA1, rgbKnownSHA1, sizeof(rgbSHA1)))
{
goto Ret;
}
// known answer test with DES - ECB
Status = TestSymmetricAlgorithm(CALG_DES, rgbDESKey, sizeof(rgbDESKey),
rgbDESKnownPlaintext,
sizeof(rgbDESKnownPlaintext),
rgbDESKnownCiphertext,
NULL);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// known answer test with DES - CBC
Status = TestSymmetricAlgorithm(CALG_DES, rgbDESKey, sizeof(rgbDESKey),
rgbDESKnownPlaintext,
sizeof(rgbDESKnownPlaintext),
rgbDESCBCCiphertext,
rgbIV);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// known answer test with 3DES - ECB
Status = TestSymmetricAlgorithm(CALG_3DES, rgb3DESKey, sizeof(rgb3DESKey),
rgb3DESKnownPlaintext,
sizeof(rgb3DESKnownPlaintext),
rgb3DESKnownCiphertext,
NULL);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// known answer test with 3DES - CBC
Status = TestSymmetricAlgorithm(CALG_3DES, rgb3DESKey, sizeof(rgb3DESKey),
rgb3DESKnownPlaintext,
sizeof(rgb3DESKnownPlaintext),
rgb3DESCBCCiphertext,
rgbIV);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// known answer test with 3DES 112 - ECB
Status = TestSymmetricAlgorithm(CALG_3DES_112, rgb3DES112Key,
sizeof(rgb3DES112Key),
rgb3DES112KnownPlaintext,
sizeof(rgb3DES112KnownPlaintext),
rgb3DES112KnownCiphertext,
NULL);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
Status = TestSymmetricAlgorithm(CALG_3DES_112, rgb3DES112Key,
sizeof(rgb3DES112Key),
rgb3DES112KnownPlaintext,
sizeof(rgb3DES112KnownPlaintext),
rgb3DES112CBCCiphertext,
rgbIV);
if (!NT_SUCCESS(Status))
{
goto Ret;
}
// Known answer test for SHA-HMAC
RtlZeroMemory(rgbSHA1, sizeof(rgbSHA1));
RtlZeroMemory(&ShaCtx, sizeof(ShaCtx));
for (ul = 0; ul < sizeof(rgbHmacData); ul++)
rgbHmacData[ul] = 0xcd;
FipsHmacSHAInit(&ShaCtx, rgbHmacKey, sizeof(rgbHmacKey));
FipsHmacSHAUpdate(&ShaCtx, rgbHmacData, sizeof(rgbHmacData));
FipsHmacSHAFinal(&ShaCtx, rgbHmacKey, sizeof(rgbHmacKey), rgbSHA1);
if (! RtlEqualMemory(rgbSHA1, rgbHmac, sizeof(rgbHmac)))
goto Ret;
Status = STATUS_SUCCESS;
Ret:
return Status;
}