///////////////////////////////////////////////////////////////////////////// // 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 #include #include #include #include #ifdef KERNEL_MODE #if WINVER == 0x0500 #include #else #include #endif #endif #include #include #include #include // 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; }