//+----------------------------------------------------------------------- // // File: DESWRAP.C // // Contents: CryptoSystem wrapper functions for DES // // // History: 06-Sep-1996 MikeSw Created // //------------------------------------------------------------------------ // // Portions of this code (the key generation code) were taken from the // MIT kerberos distribution. // /* * * Copyright 1989,1990 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * require a specific license from the United States Government. * It is the responsibility of any person or organization contemplating * export to obtain such a license before exporting. * * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and * distribute this software and its documentation for any purpose and * without fee is hereby granted, provided that the above copyright * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. * * * Under U.S. law, this software may not be exported outside the US * without license from the U.S. Commerce department. * * These routines form the library interface to the DES facilities. * * Originally written 8/85 by Steve Miller, MIT Project Athena. */ /* des.c - Routines for implementing the FIPS Data Encryption Standard (DES). * * Allan Bjorklund, University of Michigan, ITD/RS/DD. * July 24, 1993. * * Revisions for PC memory model portability, July 11, 1994. * * Removed model portability header and added Win95 DLL * declarations, May 31, 1995. * * Made all declarations Win95 and NT specific, September 18, 1995. * * Added quad_cksum, October 9, 1995. * * Copyright (c) 1995,1996 Regents of The University of Michigan. * All Rights Reserved. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appears in all copies and * that both that copyright notice and this permission notice appear * in supporting documentation, and that the name of The University * of Michigan not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. This software is supplied as is without expressed or * implied warranties of any kind. * * Research Systems Unix Group * The University of Michigan * c/o Allan Bjorklund * 535 W. William Street * Ann Arbor, Michigan * kerb95@umich.edu */ #ifndef KERNEL_MODE #include #include #include #include #else #include #include #endif #include #include #include #include #include #include #ifdef WIN32_CHICAGO #include #undef ASSERT #define ASSERT(x) assert(x) VOID MyRtlFreeOemString( POEM_STRING OemString ); #define RtlFreeOemString(x) MyRtlFreeOemString(x) NTSTATUS MyRtlUnicodeStringToOemString( OUT POEM_STRING DestinationString, IN PUNICODE_STRING SourceString, IN BOOLEAN AllocateDestinationString ); #define RtlUnicodeStringToOemString(x, y, z) MyRtlUnicodeStringToOemString(x, y, z) #endif // WIN32_CHICAGO #include "modes.h" #include "des.h" #include "md5.h" BOOLEAN md5Hmac( IN PUCHAR pbKeyMaterial, IN ULONG cbKeyMaterial, IN PUCHAR pbData, IN ULONG cbData, IN PUCHAR pbData2, IN ULONG cbData2, OUT PUCHAR HmacData ); #define DES_CONFOUNDER_LEN 8 typedef struct _DES_HEADER { UCHAR Confounder[DES_CONFOUNDER_LEN]; UCHAR Checksum[MD5_LEN]; } DES_HEADER, *PDES_HEADER; typedef struct _DES_STATE_BUFFER { PCHECKSUM_FUNCTION ChecksumFunction; DESTable KeyTable; UCHAR InitializationVector[DES_BLOCKLEN]; } DES_STATE_BUFFER, *PDES_STATE_BUFFER; typedef struct _DES_MAC_STATE_BUFFER { DESTable KeyTable; UCHAR Confounder[DES_BLOCKLEN]; UCHAR InitializationVector[DES_BLOCKLEN]; } DES_MAC_STATE_BUFFER, *PDES_MAC_STATE_BUFFER; typedef struct _DES_MAC_1510_STATE_BUFFER { DESTable KeyTable; UCHAR InitializationVector[DES_BLOCKLEN]; UCHAR Confounder[DES_BLOCKLEN]; DESTable FinalKeyTable; } DES_MAC_1510_STATE_BUFFER, *PDES_MAC_1510_STATE_BUFFER; NTSTATUS NTAPI desPlainInitialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI desPlainExpInitialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI desMd5Initialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI desMd5ExpInitialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI desCrc32Initialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI desEncrypt(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG); NTSTATUS NTAPI desDecrypt(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG); NTSTATUS NTAPI desFinish(PCRYPT_STATE_BUFFER *); NTSTATUS NTAPI desHashPassword(PSECURITY_STRING, PUCHAR); NTSTATUS NTAPI desInitRandom(ULONG); NTSTATUS NTAPI desRandomKey(PUCHAR, ULONG, PUCHAR); NTSTATUS NTAPI desFinishRandom(void); NTSTATUS NTAPI desControl(ULONG, PCRYPT_STATE_BUFFER, PUCHAR, ULONG); NTSTATUS NTAPI desMacGeneralInitializeEx(PUCHAR, ULONG, PUCHAR, ULONG, PCHECKSUM_BUFFER *); NTSTATUS NTAPI desMacInitialize(ULONG, PCHECKSUM_BUFFER *); NTSTATUS NTAPI desMacInitializeEx(PUCHAR,ULONG, ULONG, PCHECKSUM_BUFFER *); NTSTATUS NTAPI desMacKInitializeEx(PUCHAR,ULONG, ULONG, PCHECKSUM_BUFFER *); NTSTATUS NTAPI desMac1510Initialize(ULONG, PCHECKSUM_BUFFER *); NTSTATUS NTAPI desMac1510InitializeEx(PUCHAR,ULONG, ULONG, PCHECKSUM_BUFFER *); NTSTATUS NTAPI desMac1510InitializeEx2(PUCHAR,ULONG, PUCHAR, ULONG, PCHECKSUM_BUFFER *); NTSTATUS NTAPI desMac1510Finalize(PCHECKSUM_BUFFER, PUCHAR); NTSTATUS NTAPI desMacSum(PCHECKSUM_BUFFER, ULONG, PUCHAR); NTSTATUS NTAPI desMacFinalize(PCHECKSUM_BUFFER, PUCHAR); NTSTATUS NTAPI desMacFinish(PCHECKSUM_BUFFER *); #ifdef KERNEL_MODE #pragma alloc_text( PAGEMSG, desPlainInitialize ) #pragma alloc_text( PAGEMSG, desPlainExpInitialize ) #pragma alloc_text( PAGEMSG, desMd5Initialize ) #pragma alloc_text( PAGEMSG, desMd5ExpInitialize ) #pragma alloc_text( PAGEMSG, desCrc32Initialize ) #pragma alloc_text( PAGEMSG, desEncrypt ) #pragma alloc_text( PAGEMSG, desDecrypt ) #pragma alloc_text( PAGEMSG, desFinish ) #pragma alloc_text( PAGEMSG, desHashPassword ) #pragma alloc_text( PAGEMSG, desInitRandom ) #pragma alloc_text( PAGEMSG, desRandomKey ) #pragma alloc_text( PAGEMSG, desFinishRandom ) #pragma alloc_text( PAGEMSG, desControl ) #pragma alloc_text( PAGEMSG, desMacInitialize ) #pragma alloc_text( PAGEMSG, desMacInitializeEx ) #pragma alloc_text( PAGEMSG, desMacSum ) #pragma alloc_text( PAGEMSG, desMacFinalize ) #pragma alloc_text( PAGEMSG, desMacFinish ) #pragma alloc_text( PAGEMSG, desMacGeneralInitializeEx ) #pragma alloc_text( PAGEMSG, desMacKInitializeEx ) #pragma alloc_text( PAGEMSG, desMac1510Initialize ) #pragma alloc_text( PAGEMSG, desMac1510InitializeEx ) #pragma alloc_text( PAGEMSG, desMac1510InitializeEx2 ) #pragma alloc_text( PAGEMSG, desMac1510Finalize ) #endif CRYPTO_SYSTEM csDES_MD5 = { KERB_ETYPE_DES_CBC_MD5, // Etype DES_BLOCKLEN, // Blocksize KERB_ETYPE_DES_CBC_MD5, // exportable version DES_KEYSIZE, // Key size, in bytes sizeof(DES_HEADER), // header size KERB_CHECKSUM_MD5, // Preferred Checksum CSYSTEM_USE_PRINCIPAL_NAME | CSYSTEM_INTEGRITY_PROTECTED | CSYSTEM_EXPORT_STRENGTH, // Attributes L"Kerberos DES-CBC-MD5", // Text name desMd5Initialize, desEncrypt, desDecrypt, desFinish, desHashPassword, desRandomKey, desControl }; CRYPTO_SYSTEM csDES_CRC32 = { KERB_ETYPE_DES_CBC_CRC, // Etype DES_BLOCKLEN, // Blocksize (stream) KERB_ETYPE_DES_CBC_CRC, // exportable version DES_KEYSIZE, // Key size, in bytes sizeof(DES_HEADER), // header size KERB_CHECKSUM_CRC32, // Preferred Checksum CSYSTEM_USE_PRINCIPAL_NAME | CSYSTEM_INTEGRITY_PROTECTED | CSYSTEM_EXPORT_STRENGTH, // Attributes L"Kerberos DES-CBC-CRC", // Text name desCrc32Initialize, desEncrypt, desDecrypt, desFinish, desHashPassword, desRandomKey, desControl }; CRYPTO_SYSTEM csDES_PLAIN = { KERB_ETYPE_DES_PLAIN, // Etype DES_BLOCKLEN, // Blocksize KERB_ETYPE_DES_PLAIN, // exportable version DES_KEYSIZE, // Key size, in bytes 0, // header size KERB_CHECKSUM_CRC32, // Preferred Checksum CSYSTEM_USE_PRINCIPAL_NAME | CSYSTEM_EXPORT_STRENGTH, // Attributes L"Kerberos DES-Plain", // Text name desPlainInitialize, desEncrypt, desDecrypt, desFinish, desHashPassword, desRandomKey, desControl }; CHECKSUM_FUNCTION csfDesMac = { KERB_CHECKSUM_DES_MAC, // Checksum type DES_BLOCKLEN, // Checksum length CKSUM_KEYED, desMacInitialize, desMacSum, desMacFinalize, desMacFinish, desMacInitializeEx, NULL}; CHECKSUM_FUNCTION csfDesMacK = { KERB_CHECKSUM_KRB_DES_MAC_K, // Checksum type DES_BLOCKLEN, // Checksum length CKSUM_KEYED, desMacInitialize, desMacSum, desMacFinalize, desMacFinish, desMacKInitializeEx, NULL}; CHECKSUM_FUNCTION csfDesMac1510 = { KERB_CHECKSUM_KRB_DES_MAC, // Checksum type DES_BLOCKLEN * 2, // Checksum length CKSUM_KEYED, desMac1510Initialize, desMacSum, desMac1510Finalize, desMacFinish, // just frees the buffer desMac1510InitializeEx, desMac1510InitializeEx2}; #define SMASK(step) ((1<>step)&SMASK(step))) #define PARITY_CHAR(x, y) \ {\ UCHAR _tmp1_, _tmp2_; \ _tmp1_ = (UCHAR) PSTEP((x),4); \ _tmp2_ = (UCHAR) PSTEP(_tmp1_,2); \ *(y) = (UCHAR) PSTEP(_tmp2_, 1); \ } \ VOID desFixupKeyParity( PUCHAR Key ) { ULONG Index; UCHAR TempChar; for (Index=0; Index < DES_BLOCKLEN; Index++) { Key[Index] &= 0xfe; PARITY_CHAR(Key[Index], &TempChar); Key[Index] |= 1 ^ TempChar; } } typedef UCHAR DES_KEYBLOCK[8]; DES_KEYBLOCK desWeakKeys[] = { /* weak keys */ {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01}, {0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}, {0x1f,0x1f,0x1f,0x1f,0x0e,0x0e,0x0e,0x0e}, {0xe0,0xe0,0xe0,0xe0,0xf1,0xf1,0xf1,0xf1}, /* semi-weak */ {0x01,0xfe,0x01,0xfe,0x01,0xfe,0x01,0xfe}, {0xfe,0x01,0xfe,0x01,0xfe,0x01,0xfe,0x01}, {0x1f,0xe0,0x1f,0xe0,0x0e,0xf1,0x0e,0xf1}, {0xe0,0x1f,0xe0,0x1f,0xf1,0x0e,0xf1,0x0e}, {0x01,0xe0,0x01,0xe0,0x01,0xf1,0x01,0xf1}, {0xe0,0x01,0xe0,0x01,0xf1,0x01,0xf1,0x01}, {0x1f,0xfe,0x1f,0xfe,0x0e,0xfe,0x0e,0xfe}, {0xfe,0x1f,0xfe,0x1f,0xfe,0x0e,0xfe,0x0e}, {0x01,0x1f,0x01,0x1f,0x01,0x0e,0x01,0x0e}, {0x1f,0x01,0x1f,0x01,0x0e,0x01,0x0e,0x01}, {0xe0,0xfe,0xe0,0xfe,0xf1,0xfe,0xf1,0xfe}, {0xfe,0xe0,0xfe,0xe0,0xfe,0xf1,0xfe,0xf1} }; /* * mit_des_is_weak_key: returns true iff key is a [semi-]weak des key. * * Requires: key has correct odd parity. */ BOOLEAN desIsWeakKey( PUCHAR Key ) { ULONG Index; DES_KEYBLOCK * WeakKey = desWeakKeys; for (Index = 0; Index < sizeof(desWeakKeys)/DES_BLOCKLEN; Index++) { if (RtlEqualMemory( WeakKey++, Key, DES_BLOCKLEN )) { return( TRUE ); } } return(FALSE); } NTSTATUS NTAPI desInitialize( PUCHAR pbKey, ULONG KeySize, ULONG MessageType, ULONG Checksum, PCRYPT_STATE_BUFFER * psbBuffer) { NTSTATUS Status; UCHAR LocalKey[DES_KEYSIZE]; PDES_STATE_BUFFER DesKey = NULL; PCHECKSUM_FUNCTION ChecksumFunction = NULL; // // Make sure we were passed an appropriate keytable // if (KeySize != DES_KEYSIZE) { return(STATUS_INVALID_PARAMETER); } RtlCopyMemory( LocalKey, pbKey, KeySize ); // // Get the appropriate checksum here. // if (Checksum != 0) { Status = CDLocateCheckSum( Checksum, &ChecksumFunction ); if (!NT_SUCCESS(Status)) { return(Status); } } else { ChecksumFunction = NULL; } // // Create the key buffer // #ifdef KERNEL_MODE DesKey = ExAllocatePool (NonPagedPool, sizeof(DES_STATE_BUFFER)); #else DesKey = LocalAlloc(0, sizeof(DES_STATE_BUFFER)); #endif if (DesKey == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } deskey(&DesKey->KeyTable, LocalKey); // // Initialize the checksum function // DesKey->ChecksumFunction = ChecksumFunction; // // DES-CBC-CRC uses the key as the ivec, MD5 and MD4 user zero // if (Checksum == KERB_CHECKSUM_CRC32) { RtlCopyMemory( DesKey->InitializationVector, LocalKey, DES_BLOCKLEN ); } else { RtlZeroMemory( DesKey->InitializationVector, DES_BLOCKLEN ); } *psbBuffer = (PCRYPT_STATE_BUFFER) DesKey; return(STATUS_SUCCESS); } #if DBG void DumpBuf( IN PUCHAR Buf, IN ULONG BufSize ) { ULONG Index; for (Index = 0; Index < BufSize ;Index++ ) { DbgPrint("%0.2x ",Buf[Index]); } } #endif NTSTATUS NTAPI desMd5Initialize( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(desInitialize( pbKey, KeySize, MessageType, KERB_CHECKSUM_MD5, psbBuffer )); } NTSTATUS NTAPI desCrc32Initialize( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(desInitialize( pbKey, KeySize, MessageType, KERB_CHECKSUM_CRC32, psbBuffer )); } NTSTATUS NTAPI desPlainInitialize( IN PUCHAR pbKey, IN ULONG KeySize, IN ULONG MessageType, OUT PCRYPT_STATE_BUFFER * psbBuffer ) { return(desInitialize( pbKey, KeySize, MessageType, 0, // no checksum psbBuffer )); } //+------------------------------------------------------------------------- // // Function: BlockDecrypt // // Synopsis: Encrypts a data buffer using DES // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: stolen from windows\base\ntcyrpto\scp\nt_crypt.c // // //-------------------------------------------------------------------------- NTSTATUS BlockEncrypt( IN PDES_STATE_BUFFER pKey, IN PUCHAR pbData, OUT PULONG pdwDataLen, IN ULONG dwBufLen ) { ULONG cbPartial, dwPadVal, dwDataLen; UCHAR pbBuf[DES_BLOCKLEN]; UCHAR FeedBack[DES_BLOCKLEN]; dwDataLen = *pdwDataLen; // // Initialize the feedback buffer to the initialization vector // memcpy( FeedBack, pKey->InitializationVector, DES_BLOCKLEN ); // // check length of the buffer and calculate the pad // (if multiple of DES_BLOCKLEN, do a full block of pad) // cbPartial = (dwDataLen % DES_BLOCKLEN); // // The original code here put in 8 bytes of padding // on an aligned buffer. That is a waste. // if (cbPartial != 0) { dwPadVal = DES_BLOCKLEN - cbPartial; } else { dwPadVal = 0; } if (pbData == NULL || dwBufLen < dwDataLen + dwPadVal) { // // set what we need // *pdwDataLen = dwDataLen + dwPadVal; if (pbData == NULL) { return (STATUS_SUCCESS); } return(STATUS_BUFFER_OVERFLOW); } // // allocate memory for a temporary buffer // // // Will this cause MIT clients/servers to flail? The caller // should pass in only buffers that are already padded to // make MIT clients work. // if (dwPadVal) { // Fill the pad with a value equal to the // length of the padding, so decrypt will // know the length of the original data // and as a simple integrity check. memset( pbData + dwDataLen, dwPadVal, dwPadVal ); } dwDataLen += dwPadVal; *pdwDataLen = dwDataLen; ASSERT((dwDataLen % DES_BLOCKLEN) == 0); // // pump the full blocks of data through // while (dwDataLen) { ASSERT(dwDataLen >= DES_BLOCKLEN); // // put the plaintext into a temporary // buffer, then encrypt the data // back into the caller's buffer // memcpy(pbBuf, pbData, DES_BLOCKLEN); CBC( des, DES_BLOCKLEN, pbData, pbBuf, &pKey->KeyTable, ENCRYPT, FeedBack ); pbData += DES_BLOCKLEN; dwDataLen -= DES_BLOCKLEN; } memcpy( pKey->InitializationVector, pbData - DES_BLOCKLEN, DES_BLOCKLEN ); return(STATUS_SUCCESS); } //+------------------------------------------------------------------------- // // Function: BlockDecrypt // // Synopsis: Decrypt a block of data encrypted with BlockEncrypt // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS BlockDecrypt( IN PDES_STATE_BUFFER pKey, IN OUT PUCHAR pbData, IN OUT PULONG pdwDataLen ) { UCHAR pbBuf[DES_BLOCKLEN]; ULONG dwDataLen, BytePos; UCHAR FeedBack[DES_BLOCKLEN]; dwDataLen = *pdwDataLen; // // Check to see if we are decrypting something already // memcpy( FeedBack, pKey->InitializationVector, DES_BLOCKLEN ); // // The data length must be a multiple of the algorithm // pad size. // if (dwDataLen % DES_BLOCKLEN) { return(STATUS_INVALID_PARAMETER); } // // pump the data through the decryption, including padding // NOTE: the total length is a multiple of DES_BLOCKLEN // for (BytePos = 0; (BytePos + DES_BLOCKLEN) <= dwDataLen; BytePos += DES_BLOCKLEN) { // // put the encrypted text into a temp buffer // memcpy (pbBuf, pbData + BytePos, DES_BLOCKLEN); CBC( des, DES_BLOCKLEN, pbData + BytePos, pbBuf, &pKey->KeyTable, DECRYPT, FeedBack ); } memcpy( pKey->InitializationVector, pbBuf, DES_BLOCKLEN ); return STATUS_SUCCESS; } NTSTATUS NTAPI desEncrypt( IN PCRYPT_STATE_BUFFER psbBuffer, IN PUCHAR pbInput, IN ULONG cbInput, OUT PUCHAR OutputBuffer, OUT PULONG OutputLength ) { NTSTATUS Status = STATUS_SUCCESS; PDES_STATE_BUFFER StateBuffer = (PDES_STATE_BUFFER) psbBuffer; PDES_HEADER CryptHeader = (PDES_HEADER) OutputBuffer; PCHECKSUM_BUFFER SumBuffer = NULL; ULONG LocalOutputLength; // // If we aren't doing raw DES, prepare a header structure // if (StateBuffer->ChecksumFunction != NULL) { // // Relocate the buffer and inserat the header // RtlMoveMemory( OutputBuffer + DES_CONFOUNDER_LEN + StateBuffer->ChecksumFunction->CheckSumSize, pbInput, cbInput ); LocalOutputLength = cbInput + DES_CONFOUNDER_LEN + StateBuffer->ChecksumFunction->CheckSumSize; // // Zero fill the padding space // RtlZeroMemory( OutputBuffer+LocalOutputLength, ROUND_UP_COUNT(LocalOutputLength,DES_BLOCKLEN) - LocalOutputLength ); LocalOutputLength = ROUND_UP_COUNT(LocalOutputLength,DES_BLOCKLEN); RtlZeroMemory( CryptHeader->Checksum, StateBuffer->ChecksumFunction->CheckSumSize ); CDGenerateRandomBits( CryptHeader->Confounder, DES_CONFOUNDER_LEN ); // // Checksum the buffer. // Status = StateBuffer->ChecksumFunction->Initialize(0, &SumBuffer); if (!NT_SUCCESS(Status)) { goto Cleanup; } StateBuffer->ChecksumFunction->Sum( SumBuffer, LocalOutputLength, OutputBuffer ); StateBuffer->ChecksumFunction->Finalize( SumBuffer, CryptHeader->Checksum ); StateBuffer->ChecksumFunction->Finish( &SumBuffer ); } else { // // Just copy the buffer // RtlCopyMemory( OutputBuffer, pbInput, cbInput ); LocalOutputLength = ROUND_UP_COUNT(cbInput,DES_BLOCKLEN); // // Zero fill the padding space // RtlZeroMemory( OutputBuffer+cbInput, LocalOutputLength - cbInput ); } // // Encrypt the buffer. // *OutputLength = LocalOutputLength; Status = BlockEncrypt( StateBuffer, OutputBuffer, OutputLength, LocalOutputLength ); Cleanup: return(Status); } NTSTATUS NTAPI desDecrypt( PCRYPT_STATE_BUFFER psbBuffer, PUCHAR pbInput, ULONG cbInput, PUCHAR pbOutput, PULONG cbOutput) { NTSTATUS Status = STATUS_SUCCESS; PDES_STATE_BUFFER StateBuffer = (PDES_STATE_BUFFER) psbBuffer; PDES_HEADER CryptHeader; UCHAR Checksum[MD5_LEN]; PCHECKSUM_BUFFER SumBuffer = NULL; // // First decrypt the whole buffer // if (*cbOutput < cbInput) { *cbOutput = cbInput; return(STATUS_BUFFER_TOO_SMALL); } RtlCopyMemory( pbOutput, pbInput, cbInput ); Status = BlockDecrypt( StateBuffer, pbOutput, &cbInput ); if (!NT_SUCCESS(Status)) { goto Cleanup; } if (StateBuffer->ChecksumFunction != NULL) { // // Now verify the checksum // CryptHeader = (PDES_HEADER) pbOutput; RtlCopyMemory( Checksum, CryptHeader->Checksum, MD5_LEN ); // // Zero the checksum field before computing the checksum of the buffer // RtlZeroMemory( CryptHeader->Checksum, StateBuffer->ChecksumFunction->CheckSumSize ); // // Checksum the buffer. // Status = StateBuffer->ChecksumFunction->Initialize(0, &SumBuffer); if (!NT_SUCCESS(Status)) { goto Cleanup; } StateBuffer->ChecksumFunction->Sum( SumBuffer, cbInput, pbOutput ); StateBuffer->ChecksumFunction->Finalize( SumBuffer, CryptHeader->Checksum ); StateBuffer->ChecksumFunction->Finish( &SumBuffer ); if (!RtlEqualMemory( CryptHeader->Checksum, Checksum, StateBuffer->ChecksumFunction->CheckSumSize )) { Status = SEC_E_MESSAGE_ALTERED; goto Cleanup; } // // Copy the input to the output without the header *cbOutput = cbInput - (DES_CONFOUNDER_LEN + StateBuffer->ChecksumFunction->CheckSumSize); RtlMoveMemory( pbOutput, pbOutput + DES_CONFOUNDER_LEN + StateBuffer->ChecksumFunction->CheckSumSize, *cbOutput ); } else { *cbOutput = cbInput; } Cleanup: return(Status); } NTSTATUS NTAPI desFinish( PCRYPT_STATE_BUFFER * psbBuffer) { PDES_STATE_BUFFER StateBuffer = (PDES_STATE_BUFFER) *psbBuffer; #ifdef KERNEL_MODE ExFreePool(StateBuffer); #else LocalFree(StateBuffer); #endif *psbBuffer = NULL; return(S_OK); } #define MIN(x,y) (((x) < (y)) ? (x) : (y)) #define XORBLOCK(x,y) \ { \ PULONG tx = (PULONG) x; \ PULONG ty = (PULONG) y; \ *tx++ ^= *ty++; \ *tx++ ^= *ty++; \ } VOID desCbcChecksum( IN PUCHAR Password, IN ULONG PasswordLength, IN PUCHAR InitialVector, IN DESTable * KeyTable, OUT PUCHAR OutputKey ) { ULONG Offset; UCHAR Feedback[DES_BLOCKLEN]; UCHAR Block[DES_BLOCKLEN]; RtlCopyMemory( Feedback, InitialVector, DES_BLOCKLEN ); for (Offset = 0; Offset < PasswordLength ; Offset+= 8 ) { RtlZeroMemory( Block, DES_BLOCKLEN ); RtlCopyMemory( Block, Password+Offset, MIN(DES_BLOCKLEN, PasswordLength - Offset) ); XORBLOCK(Block, Feedback); des( Feedback, Block, KeyTable, ENCRYPT ); } RtlCopyMemory( OutputKey, Feedback, DES_BLOCKLEN ); } #define BITREVERSE(c) ((UCHAR)((((c & 0x01) ? 0x80 : 0x00)\ |((c & 0x02) ? 0x40 : 0x00)\ |((c & 0x04) ? 0x20 : 0x00)\ |((c & 0x08) ? 0x10 : 0x00)\ |((c & 0x10) ? 0x08 : 0x00)\ |((c & 0x20) ? 0x04 : 0x00)\ |((c & 0x40) ? 0x02 : 0x00))\ & 0xFE)) // // This is the core routine that converts a buffer into a key. It is called // by desHashPassword and desRandomKey // VOID desHashBuffer( IN PUCHAR LocalPassword, IN ULONG PasswordLength, IN OUT PUCHAR Key ) { ULONG Index; BOOLEAN Forward; PUCHAR KeyPointer = Key; DESTable KeyTable; RtlZeroMemory( Key, DES_BLOCKLEN ); // // Initialize our temporary parity vector // // // Start fanfolding the bytes into the key // Forward = TRUE; KeyPointer = Key; for (Index = 0; Index < PasswordLength ; Index++ ) { if (!Forward) { *(--KeyPointer) ^= BITREVERSE(LocalPassword[Index] & 0x7F); } else { *KeyPointer++ ^= (LocalPassword[Index] & 0x7F) << 1; } if (((Index+1) & 0x07) == 0) /* When MOD 8 equals 0 */ { Forward = !Forward; /* Change direction. */ } } // // Fix key parity // desFixupKeyParity(Key); // // Check for weak keys. // if (desIsWeakKey(Key)) { Key[7] ^= 0xf0; } // // Now calculate the des-cbc-mac of the original string // deskey(&KeyTable, Key); // // Now compute the CBC checksum of the string // desCbcChecksum( LocalPassword, PasswordLength, Key, // initial vector &KeyTable, Key // output key ); // // Fix key parity // desFixupKeyParity(Key); // // Check for weak keys. // if (desIsWeakKey(Key)) { Key[7] ^= 0xf0; } } NTSTATUS NTAPI desHashPassword( IN PSECURITY_STRING Password, OUT PUCHAR Key ) { PUCHAR LocalPassword = NULL; ULONG PasswordLength; OEM_STRING OemPassword; NTSTATUS Status; // // First convert the UNICODE string to an OEM string // Status = RtlUnicodeStringToOemString( &OemPassword, Password, TRUE // allocate destination ); if (!NT_SUCCESS(Status)) { return(Status); } // // We hash the password according to RFC1510 // // This code is derived from the MIT Kerberos code in string2key.c // PasswordLength = ROUND_UP_COUNT(OemPassword.Length,8); #ifdef KERNEL_MODE LocalPassword = (PUCHAR) ExAllocatePool(NonPagedPool, PasswordLength); #else LocalPassword = (PUCHAR) LocalAlloc(0, PasswordLength); #endif if (LocalPassword == NULL) { RtlFreeOemString( &OemPassword ); return(STATUS_INSUFFICIENT_RESOURCES); } RtlCopyMemory( LocalPassword, OemPassword.Buffer, OemPassword.Length ); // // Zero extend the password // RtlZeroMemory( LocalPassword + OemPassword.Length, PasswordLength - OemPassword.Length ); // // Initialize our temporary parity vector // desHashBuffer( LocalPassword, PasswordLength, Key ); RtlFreeOemString( &OemPassword ); #ifdef KERNEL_MODE ExFreePool(LocalPassword); #else LocalFree(LocalPassword); #endif return(STATUS_SUCCESS); } NTSTATUS NTAPI desRandomKey( IN OPTIONAL PUCHAR Seed, IN ULONG SeedLength, OUT PUCHAR pbKey) { UCHAR Buffer[16]; do { CDGenerateRandomBits(Buffer,16); desHashBuffer( Buffer, 16, pbKey ); } while (desIsWeakKey(pbKey)); return(STATUS_SUCCESS); } NTSTATUS NTAPI desControl( IN ULONG Function, IN PCRYPT_STATE_BUFFER StateBuffer, IN PUCHAR InputBuffer, IN ULONG InputBufferSize ) { PDES_STATE_BUFFER DesStateBuffer = (PDES_STATE_BUFFER) StateBuffer; if (Function != CRYPT_CONTROL_SET_INIT_VECT) { return(STATUS_INVALID_PARAMETER); } if (InputBufferSize != DES_BLOCKLEN) { return(STATUS_INVALID_PARAMETER); } memcpy( DesStateBuffer->InitializationVector, InputBuffer, DES_BLOCKLEN ); return(STATUS_SUCCESS); } /////////////////////////////////////////////////////////////////////////// NTSTATUS NTAPI desMacGeneralInitializeEx( PUCHAR Key, ULONG KeySize, PUCHAR IV, ULONG MessageType, PCHECKSUM_BUFFER * ppcsBuffer ) { PDES_MAC_STATE_BUFFER DesKey = NULL; // // Make sure we were passed an appropriate keytable // if (KeySize != DES_KEYSIZE) { return(STATUS_INVALID_PARAMETER); } #ifdef KERNEL_MODE DesKey = ExAllocatePool(NonPagedPool, sizeof(DES_MAC_STATE_BUFFER)); #else DesKey = LocalAlloc(0, sizeof(DES_MAC_STATE_BUFFER)); #endif if (DesKey == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } // // Create the key buffer // deskey(&DesKey->KeyTable, Key); RtlCopyMemory( DesKey->InitializationVector, IV, DES_BLOCKLEN ); *ppcsBuffer = (PCHECKSUM_BUFFER) DesKey; return(STATUS_SUCCESS); } NTSTATUS NTAPI desMacInitializeEx( PUCHAR Key, ULONG KeySize, ULONG MessageType, PCHECKSUM_BUFFER * ppcsBuffer ) { UCHAR IV[DES_BLOCKLEN]; RtlZeroMemory( IV, DES_BLOCKLEN ); return desMacGeneralInitializeEx( Key, KeySize, IV, MessageType, ppcsBuffer ); } NTSTATUS NTAPI desMacKInitializeEx( PUCHAR Key, ULONG KeySize, ULONG MessageType, PCHECKSUM_BUFFER * ppcsBuffer ) { return desMacGeneralInitializeEx( Key, KeySize, Key, MessageType, ppcsBuffer ); } NTSTATUS NTAPI desMacInitialize(ULONG dwSeed, PCHECKSUM_BUFFER * ppcsBuffer) { return(STATUS_NOT_IMPLEMENTED); } // // NOTE - This function is used with both DES_MAC_STATE_BUFFER and // DES_MAC_1510_STATE_BUFFER as the pcsBuffer parameter, since the // DES_MAC_1510_STATE_BUFFER is the same as DES_MAC_STATE_BUFFER // except with an added confounder this should be OK. // NTSTATUS NTAPI desMacSum( PCHECKSUM_BUFFER pcsBuffer, ULONG cbData, PUCHAR pbData) { PDES_MAC_STATE_BUFFER DesKey = (PDES_MAC_STATE_BUFFER) pcsBuffer; UCHAR FeedBack[DES_BLOCKLEN]; UCHAR TempBuffer[DES_BLOCKLEN]; UCHAR OutputBuffer[DES_BLOCKLEN]; ULONG Index; // // Set up the IV for this round - it may be zero or the output of // a previous MAC // memcpy( FeedBack, DesKey->InitializationVector, DES_BLOCKLEN ); for (Index = 0; Index < cbData ; Index += DES_BLOCKLEN ) { // // Compute the input buffer, with padding // if (Index+DES_BLOCKLEN > cbData) { memset( TempBuffer, 0, DES_BLOCKLEN ); memcpy( TempBuffer, pbData, Index & (DES_BLOCKLEN-1) ); } else { memcpy( TempBuffer, pbData+Index, DES_BLOCKLEN ); } CBC( des, DES_BLOCKLEN, TempBuffer, OutputBuffer, &DesKey->KeyTable, ENCRYPT, FeedBack ); } // // Copy the feedback back into the IV for the next round // memcpy( DesKey->InitializationVector, FeedBack, DES_BLOCKLEN ); return(STATUS_SUCCESS); } NTSTATUS NTAPI desMacFinalize( PCHECKSUM_BUFFER pcsBuffer, PUCHAR pbSum) { PDES_MAC_STATE_BUFFER DesKey = (PDES_MAC_STATE_BUFFER) pcsBuffer; memcpy(pbSum, DesKey->InitializationVector, DES_BLOCKLEN); return(STATUS_SUCCESS); } NTSTATUS NTAPI desMacFinish( PCHECKSUM_BUFFER * ppcsBuffer) { #ifdef KERNEL_MODE ExFreePool(*ppcsBuffer); #else LocalFree(*ppcsBuffer); #endif *ppcsBuffer = 0; return(STATUS_SUCCESS); } NTSTATUS NTAPI desMac1510Initialize(ULONG dwSeed, PCHECKSUM_BUFFER * ppcsBuffer) { return(STATUS_NOT_IMPLEMENTED); } NTSTATUS NTAPI desMac1510InitializeEx( PUCHAR Key, ULONG KeySize, ULONG MessageType, PCHECKSUM_BUFFER * ppcsBuffer ) { return(STATUS_NOT_IMPLEMENTED); } NTSTATUS NTAPI desMac1510InitializeEx2( PUCHAR Key, ULONG KeySize, PUCHAR ChecksumToVerify, ULONG MessageType, PCHECKSUM_BUFFER * ppcsBuffer ) { ULONG *pul; ULONG *pul2; UCHAR FinalKey[DES_KEYSIZE]; PDES_MAC_1510_STATE_BUFFER DesKey = NULL; // // Make sure we were passed an appropriate keytable // if (KeySize != DES_KEYSIZE) { return(STATUS_INVALID_PARAMETER); } #ifdef KERNEL_MODE DesKey = ExAllocatePool(NonPagedPool, sizeof(DES_MAC_1510_STATE_BUFFER)); #else DesKey = LocalAlloc(0, sizeof(DES_MAC_1510_STATE_BUFFER)); #endif if (DesKey == NULL) { return(STATUS_INSUFFICIENT_RESOURCES); } // // create the final key table // pul = (ULONG*)FinalKey; pul2 = (ULONG*)Key; *pul = *pul2 ^ 0xf0f0f0f0; pul = (ULONG*)(FinalKey + sizeof(ULONG)); pul2 = (ULONG*)(Key + sizeof(ULONG)); *pul = *pul2 ^ 0xf0f0f0f0; deskey(&DesKey->FinalKeyTable, FinalKey); // // Checksum was not passed in so generate a confounder // if (NULL == ChecksumToVerify) { CDGenerateRandomBits(DesKey->Confounder,DES_BLOCKLEN); } else { // the IV is all zero so no need to use CBC on first block des(DesKey->Confounder, ChecksumToVerify, &DesKey->FinalKeyTable, DECRYPT); } // // Create the key buffer // deskey(&DesKey->KeyTable, Key); // the IV is all zero so no need to use CBC on first block, but the // ecncrypted confounder becomes the next IV des(DesKey->InitializationVector, DesKey->Confounder, &DesKey->KeyTable, ENCRYPT); *ppcsBuffer = (PCHECKSUM_BUFFER) DesKey; return(STATUS_SUCCESS); } NTSTATUS NTAPI desMac1510Finalize( PCHECKSUM_BUFFER pcsBuffer, PUCHAR pbSum) { UCHAR Feedback[DES_BLOCKLEN]; PDES_MAC_1510_STATE_BUFFER DesKey = (PDES_MAC_1510_STATE_BUFFER) pcsBuffer; // the IV is all zero so no need to use CBC on first block des(Feedback, DesKey->Confounder, &DesKey->FinalKeyTable, ENCRYPT); memcpy(pbSum, Feedback, DES_BLOCKLEN); // use CBC on second block CBC( des, DES_BLOCKLEN, pbSum + DES_BLOCKLEN, DesKey->InitializationVector, &DesKey->FinalKeyTable, ENCRYPT, Feedback ); return(STATUS_SUCCESS); }