//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 2000 // // File: nonce.cxx // // Contents: Context APIs for the Digest security package // Main entry points into this dll: // NonceCreate // NonceValidate // NonceInitialize // // History: ChandanS 26-Jul-1996 Stolen from kerberos\client2\ctxtapi.cxx // KDamour 16Mar00 Stolen from NTLM ctxtapi.cxx // //------------------------------------------------------------------------ #include #include // Hold the Hex representation plus the NULL char g_cNoncePrivateKey[(2*NONCE_PRIVATE_KEY_BYTESIZE) + 1]; // // Globals // HCRYPTPROV g_hCryptProv = 0; // Handle for CryptoAPI WORD g_SupportedCrypto = 0; // Supported Crypt Functions bitmask (i.e. SUPPORT_DES) // Needed for Digest Calculation and Nonce Hash char *pbSeparator = COLONSTR; //+-------------------------------------------------------------------- // // Function: NonceInitialize // // Synopsis: This function is to be called // // Arguments: None // // Returns: // // Notes: // CryptReleaseContext( g_hCryptProv, 0 ) to release the cypt context // //--------------------------------------------------------------------- NTSTATUS NTAPI NonceInitialize( VOID ) { NTSTATUS Status = STATUS_SUCCESS; BYTE abTemp[NONCE_PRIVATE_KEY_BYTESIZE]; HCRYPTKEY hKey = 0; DebugLog((DEB_TRACE_FUNC, "NonceInitialize: Entering\n")); if (g_hCryptProv) { // Catch cases where LSA and Usermode running in same addr space DebugLog((DEB_TRACE, "NonceInitialize: Already Inited Leaving\n")); return STATUS_SUCCESS; } // // Get a handle to the CSP we'll use for all our hash functions etc // if ( !CryptAcquireContext( &g_hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) ) { DebugLog((DEB_ERROR, "NonceInitialize:CryptCreateHash() failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; return(Status); } // // Generate and copy over the random bytes // if ( !CryptGenRandom( g_hCryptProv, NONCE_PRIVATE_KEY_BYTESIZE, abTemp ) ) { DebugLog((DEB_ERROR, "NonceInitialize:NonceInitialize CryptGenRandom() failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; return (Status); } BinToHex((LPBYTE) abTemp, NONCE_PRIVATE_KEY_BYTESIZE, (LPSTR) g_cNoncePrivateKey); SetSupportedCrypto(); DebugLog((DEB_TRACE_FUNC, "NonceInitialize:Leaving NonceInitialize\n")); return (Status); } //+-------------------------------------------------------------------- // // Function: SetSupportedCrypto // // Synopsis: Set the bitmask for the supported crypto CSP installed // // Arguments: none // // Returns: STATUS_DATA_ERROR - error in reading CSP capabilities // STATUS_SUCCESS - operation completed normally // // //--------------------------------------------------------------------- NTSTATUS NTAPI SetSupportedCrypto(VOID) { NTSTATUS Status = STATUS_SUCCESS; g_SupportedCrypto = SUPPORT_3DES | SUPPORT_DES | SUPPORT_RC4_40 | SUPPORT_RC4 | SUPPORT_RC4_56; // FIXFIX use CryptGetProvParam to set to actual installed CSP return(Status); } //+-------------------------------------------------------------------- // // Function: NonceCreate // // Synopsis: This function is to be called once during User Mode initialization // // Arguments: pczNonce - pointer to a STRING to fillin // with a new nonce // // Returns: STATUS_DATA_ERROR - input NONCE not enough space // STATUS_SUCCESS - operation completed normally // // Notes: Function will return error if Nonce UNICODE_STRING is not empty // NONCE FORMAT // rand-data = rand[16] // nonce_binary = time-stamp rand-data H(time-stamp ":" rand-data ":" nonce_private_key) // nonce = hex(nonce_binary) // //--------------------------------------------------------------------- NTSTATUS NTAPI NonceCreate( IN OUT PSTRING pstrNonce ) { NTSTATUS Status = STATUS_SUCCESS; BYTE abRandomData[RANDDATA_BYTESIZE]; char acRandomHex[(2*RANDDATA_BYTESIZE) + 1]; int cbNonce = 0; time_t tcurrent = time(NULL); DebugLog((DEB_TRACE_FUNC, "NonceCreate: Entering\n")); // Check to make sure that there is enough space on ouput string // Need room for the Nonce and the NULL terminator if (!pstrNonce->Buffer) { Status = StringAllocate(pstrNonce, NONCE_SIZE); if (!NT_SUCCESS (Status)) { Status = SEC_E_INSUFFICIENT_MEMORY; DebugLog((DEB_ERROR, "NonceCreate: StringAllocate error 0x%x\n", Status)); goto CleanUp; } } if (pstrNonce->MaximumLength < (NONCE_SIZE + 1)) { DebugLog((DEB_ERROR, "NonceCreate: Input STRING too small\n")); Status = STATUS_BUFFER_TOO_SMALL; goto CleanUp; } // Copy over the current time BinToHex((LPBYTE)&tcurrent, sizeof(time_t), (LPSTR) pstrNonce->Buffer); cbNonce += (sizeof(time_t) * 2); // // Generate and copy over the random bytes // if ( !CryptGenRandom( g_hCryptProv, RANDDATA_BYTESIZE, abRandomData ) ) { DebugLog((DEB_TRACE, "NonceCreate: CryptGenRandom() failed : 0x%x\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; return (Status); } // // Convert to ASCII, doubling the length, and add to nonce // BinToHex( abRandomData, RANDDATA_BYTESIZE, acRandomHex); memcpy(pstrNonce->Buffer + NONCE_RANDDATA_LOC, acRandomHex, (2 * NONCE_PRIVATE_KEY_BYTESIZE)); // // Now calculate the Hash. It will be NULL terminated but STRING length does not include NULL // Status = NonceHash((LPBYTE) pstrNonce->Buffer, (2 * sizeof(time_t)), (LPBYTE) acRandomHex, (2 * RANDDATA_BYTESIZE), (LPBYTE) g_cNoncePrivateKey, (2 * NONCE_PRIVATE_KEY_BYTESIZE), (LPBYTE) (pstrNonce->Buffer + NONCE_HASH_LOC)); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "NonceCreate: failed %d\n", Status)); goto CleanUp; } pstrNonce->Length = NONCE_SIZE; CleanUp: if (!NT_SUCCESS(Status)) { pstrNonce->Length = 0; } DebugLog((DEB_TRACE_FUNC, "NonceCreate: Leaving\n")); return (Status); } //+-------------------------------------------------------------------- // // Function: NonceIsValid // // Synopsis: Called with a pointer to a Nonce and returns NTSTATUS This is the // main function that checks for a valid nonce. // // Arguments: None // // Returns: NTSTATUS STATUS_SUCCESS if NONCE generated locally and is valid // // Notes: // //--------------------------------------------------------------------- NTSTATUS NonceIsValid( PSTRING pstrNonce ) { NTSTATUS Status = STATUS_SUCCESS; DebugLog((DEB_TRACE_FUNC, "NonceIsValid: Entering\n")); // Check the size first if (pstrNonce->Length != NONCE_SIZE) { DebugLog((DEB_ERROR, "NonceIsValid: Incorrect size for the Nonce\n")); return STATUS_UNSUCCESSFUL; } if (!pstrNonce->Buffer) { DebugLog((DEB_ERROR, "NonceIsValid: NULL pointer for the Nonce\n")); return STATUS_UNSUCCESSFUL; } if (NonceIsTampered(pstrNonce)) { DebugLog((DEB_ERROR, "NonceIsValid: Nonce hash does not match\n")); return STATUS_UNSUCCESSFUL; } DebugLog((DEB_TRACE_FUNC, "NonceIsValid: Leaving\n")); return (Status); } /*++ Routine Description: Creates MD5 hash of input buffer Arguments: pbData - data to hash cbData - size of data pointed to by pbData pbHash - buffer that receives hash; is assumed to be big enough to contain MD5 hash Return Value: TRUE if successful, FALSE if not --*/ BOOL HashData( BYTE *pbData, DWORD cbData, BYTE *pbHash ) { HCRYPTHASH hHash = NULL; DebugLog((DEB_TRACE_FUNC, "HashData: Entering\n")); if ( !CryptCreateHash( g_hCryptProv, CALG_MD5, 0, 0, &hHash ) ) { DebugLog((DEB_ERROR, "HashData: CryptCreateHash failed : 0x%lx\n", GetLastError())); return FALSE; } if ( !CryptHashData( hHash, pbData, cbData, 0 ) ) { DebugLog((DEB_ERROR, "HashData: CryptCreateHash failed : 0x%lx\n", GetLastError())); CryptDestroyHash( hHash ); return FALSE; } DWORD cbHash = MD5_HASH_BYTESIZE; if ( !CryptGetHashParam( hHash, HP_HASHVAL, pbHash, &cbHash, 0 ) ) { DebugLog((DEB_ERROR, "HashData: CryptCreateHash failed : 0x%lx\n", GetLastError())); CryptDestroyHash( hHash ); return FALSE; } CryptDestroyHash( hHash ); DebugLog((DEB_TRACE_FUNC, "HashData: Leaving\n")); return TRUE; } /*++ Routine Description: Creates MD5 hash of the Nonce values Arguments: pbTime - pointer to char buffer encoded Time() cbTime - number of bytes in encoded Time() buffer to process pbRandom - pointer to char buffer encoded random sequence cbRandom - number of bytes in encoded random buffer to process pbTKey - pointer to char buffer encoded private key cbKey - number of bytes in encoded private key buffer to process pbHash - pointer to char buffer encoded Nonce Hash cbHash - number of bytes in encoded Time() buffer to process Return Value: STATUS_SUCCESS - normal completion --*/ NTSTATUS NTAPI NonceHash( IN LPBYTE pbTime, IN DWORD cbTime, IN LPBYTE pbRandom, IN DWORD cbRandom, IN LPBYTE pbKey, IN DWORD cbKey, OUT LPBYTE pbHash) { NTSTATUS Status = STATUS_SUCCESS; HCRYPTHASH hHash = NULL; DWORD cbHash = MD5_HASH_BYTESIZE; // Number of bytes for MD5 hash unsigned char abHashBin[MD5_HASH_BYTESIZE]; DebugLog((DEB_TRACE, "NonceHash: Entering\n")); if ( !CryptCreateHash( g_hCryptProv, CALG_MD5, 0, 0, &hHash ) ) { DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } if ( !CryptHashData( hHash, pbTime, cbTime, 0 ) ) { DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } if ( !CryptHashData( hHash, (const unsigned char *)pbSeparator, COLONSTR_LEN, 0 ) ) { DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } if ( !CryptHashData( hHash, pbRandom, cbRandom, 0 ) ) { DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } if ( !CryptHashData( hHash, (const unsigned char *)pbSeparator, COLONSTR_LEN, 0 ) ) { DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } if ( !CryptHashData( hHash, pbKey, cbKey, 0 ) ) { DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } if ( !CryptGetHashParam( hHash, HP_HASHVAL, abHashBin, &cbHash, 0 ) ) { DebugLog((DEB_ERROR, "NonceHash: CryptCreateHash failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; goto CleanUp; } // Now convert the Hash to hex BinToHex(abHashBin, MD5_HASH_BYTESIZE, (char *)pbHash); CleanUp: if (hHash) { CryptDestroyHash( hHash ); hHash = NULL; } DebugLog((DEB_TRACE_FUNC, "NonceHash: Leaving\n")); return(Status); } //+-------------------------------------------------------------------- // // Function: NonceIsExpired // // Synopsis: Check the timestamp to make sure that Nonce is still valid // // Arguments: None // // Returns: TRUE/FALSE if Nonce is expired // // Notes: Called from NonceIsValid // //--------------------------------------------------------------------- BOOL NonceIsExpired(PSTRING pstrNonce) { DebugLog((DEB_TRACE_FUNC, "NonceIsExpired: Entering\n")); time_t tcurrent = time(NULL); time_t tnonce = 0; // time-stamp is the first bytes in the nonce HexToBin(pstrNonce->Buffer, (2*TIMESTAMP_BYTESIZE), (unsigned char *)&tnonce); // If LifeTime set to zero - nonces never expire if (((unsigned long)tnonce > (unsigned long)tcurrent) || (g_dwParameter_Lifetime && (((unsigned long)tcurrent - (unsigned long)tnonce) > g_dwParameter_Lifetime))) { DebugLog((DEB_TRACE_FUNC, "NonceIsExpired: NonceHash has expired. Expired = TRUE\n")); return TRUE; } DebugLog((DEB_TRACE_FUNC, "NonceIsExpired: Leaving Expired = FALSE\n")); return FALSE; } //+-------------------------------------------------------------------- // // Function: NonceIsTampered // // Synopsis: Check the hash matches for the Nonce // // Arguments: None // // Returns: TRUE/FALSE if Nonce hash fails check // // Notes: Called from NonceIsValid // //--------------------------------------------------------------------- BOOL NonceIsTampered(PSTRING pstrNonce) { BOOL bStatus = FALSE; NTSTATUS Status = STATUS_SUCCESS; unsigned char abHashHex[(2*MD5_HASH_BYTESIZE) + 1]; DebugLog((DEB_TRACE_FUNC, "NonceIsTampered:Entering \n")); Status = NonceHash((LPBYTE) (pstrNonce->Buffer + NONCE_TIME_LOC), (2 * sizeof(time_t)), (LPBYTE) (pstrNonce->Buffer + NONCE_RANDDATA_LOC), (2 * RANDDATA_BYTESIZE), (LPBYTE) g_cNoncePrivateKey, (2 * NONCE_PRIVATE_KEY_BYTESIZE), (LPBYTE) abHashHex); if (!NT_SUCCESS (Status)) { DebugLog((DEB_ERROR, "NonceIsTampered: NonceHash has failed %d\n", Status)); bStatus = TRUE; goto CleanUp; } if (memcmp(abHashHex, (pstrNonce->Buffer + NONCE_HASH_LOC), (2 * MD5_HASH_BYTESIZE))) { DebugLog((DEB_ERROR, "NonceIsTampered: memcmp failed\n")); bStatus = TRUE; goto CleanUp; } CleanUp: DebugLog((DEB_TRACE_FUNC, "NonceIsTampered: Leaving\n")); return bStatus; } //+-------------------------------------------------------------------- // // Function: OpaqueCreate // // Synopsis: Creates an Opaque string composed of OPAQUE_SIZE of random data // // Arguments: pstrOpque - pointer to a STRING to fillin // with a new opaque // // Returns: STATUS_DATA_ERROR - input NONCE not enough space // STATUS_SUCCESS - operation completed normally // // Notes: Function will return error if Nonce STRING is not empty // OPAQUE FORMAT // opaque_binary = rand[OPAQUE_SIZE] // nonce = Hex(opaque_binary) // //--------------------------------------------------------------------- NTSTATUS NTAPI OpaqueCreate( IN OUT PSTRING pstrOpaque ) { NTSTATUS Status = STATUS_SUCCESS; BYTE abRandomData[OPAQUE_RANDATA_SIZE]; char acRandomHex[(2*OPAQUE_RANDATA_SIZE) + 1]; int cbNonce = 0; DebugLog((DEB_TRACE_FUNC, "OpaqueCreate: Entering\n")); // Check to make sure that there is enough space on ouput string // Need room for the Nonce and the NULL terminator if (!pstrOpaque->Buffer) { Status = StringAllocate(pstrOpaque, OPAQUE_SIZE); if (!NT_SUCCESS (Status)) { Status = SEC_E_INSUFFICIENT_MEMORY; DebugLog((DEB_ERROR, "OpaqueCreate: StringAllocate error 0x%x\n", Status)); goto CleanUp; } } else if (pstrOpaque->MaximumLength < ((2 * OPAQUE_RANDATA_SIZE) + 1)) { DebugLog((DEB_ERROR, "OpaqueCreate: Input STRING too small\n")); Status = STATUS_BUFFER_TOO_SMALL; goto CleanUp; } // // Generate and copy over the random bytes // if ( !CryptGenRandom( g_hCryptProv, OPAQUE_RANDATA_SIZE, abRandomData ) ) { DebugLog((DEB_TRACE, "OpaqueCreate: CryptGenRandom() failed : 0x%lx\n", GetLastError())); Status = STATUS_INTERNAL_ERROR; return (Status); } // // Convert to ASCII, doubling the length, and add to nonce // BinToHex( abRandomData, OPAQUE_RANDATA_SIZE, pstrOpaque->Buffer); pstrOpaque->Length = (2 * OPAQUE_RANDATA_SIZE); CleanUp: if (!NT_SUCCESS(Status)) { pstrOpaque->Length = 0; } DebugLog((DEB_TRACE_FUNC, "OpaqueCreate: Leaving\n")); return (Status); }