/*----------------------------------------------------------------------------- * Copyright (C) Microsoft Corporation, 1995 - 1999 * All rights reserved. * * This file is part of the Microsoft Private Communication Technology * reference implementation, version 1.0 * * The Private Communication Technology reference implementation, version 1.0 * ("PCTRef"), is being provided by Microsoft to encourage the development and * enhancement of an open standard for secure general-purpose business and * personal communications on open networks. Microsoft is distributing PCTRef * at no charge irrespective of whether you use PCTRef for non-commercial or * commercial use. * * Microsoft expressly disclaims any warranty for PCTRef and all derivatives of * it. PCTRef and any related documentation is provided "as is" without * warranty of any kind, either express or implied, including, without * limitation, the implied warranties or merchantability, fitness for a * particular purpose, or noninfringement. Microsoft shall have no obligation * to provide maintenance, support, upgrades or new releases to you or to anyone * receiving from you PCTRef or your modifications. The entire risk arising out * of use or performance of PCTRef remains with you. * * Please see the file LICENSE.txt, * or http://pct.microsoft.com/pct/pctlicen.txt * for more information on licensing. * * Please see http://pct.microsoft.com/pct/pct.htm for The Private * Communication Technology Specification version 1.0 ("PCT Specification") * * 1/23/96 *----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------- * RSA Public Key Cryptosystem, RC4, MD2, MD5 and RSA are trademarks * of RSA Data Security, Inc. *----------------------------------------------------------------------------*/ #include #include #ifdef __cplusplus extern "C" { #endif #include #include #include #ifdef __cplusplus } #endif static unsigned char MD5_PRELUDE[] = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10 }; static unsigned char MD2_PRELUDE[] = { 0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x02, 0x05, 0x00, 0x04, 0x10 }; static VOID ReverseMemCopy( PUCHAR Dest, PUCHAR Source, ULONG Size) { PUCHAR p; p = Dest + Size - 1; do { *p-- = *Source++; } while (p >= Dest); } BOOL WINAPI SigRSAMD2Sign( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD *pcbSigned, PctPrivateKey *pKey); BOOL WINAPI SigRSAMD5Sign( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD *pcbSigned, PctPrivateKey *pKey); BOOL WINAPI SigRSASHAMD5Sign( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD *pcbSigned, PctPrivateKey *pKey); BOOL WINAPI SigRSAMD2Verify( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD cbSigned, PctPublicKey *pKey); BOOL WINAPI SigRSAMD5Verify( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD cbSigned, PctPublicKey *pKey); BOOL WINAPI SigRSASHAMD5Verify( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD cbSigned, PctPublicKey *pKey); #define ADVAPI32_PATH TEXT("advapi32.dll") #define CRYPT_CREATE_HASH_NAME TEXT("CryptCreateHash") #define CRYPT_HASH_DATA_NAME TEXT("CryptHashData") #define CRYPT_SIGN_HASH_NAMEA TEXT("CryptSignHashA") #define CRYPT_SIGN_HASH_NAMEW TEXT("CryptSignHashW") #define CRYPT_DESTROY_HASH_NAME TEXT("CryptDestroyHash") #define CRYPT_SET_HASH_PARAM_NAME TEXT("CryptSetHashParam") typedef BOOL ( WINAPI * CRYPT_CREATE_HASH_FN)( HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash); typedef BOOL ( WINAPI * CRYPT_HASH_DATA_FN)( HCRYPTHASH hHash, CONST BYTE *pbData, DWORD dwDataLen, DWORD dwFlags); typedef BOOL ( WINAPI * CRYPT_DESTROY_HASH_FN) ( HCRYPTHASH hHash); typedef BOOL ( WINAPI * CRYPT_SIGN_HASH_FNA)( HCRYPTHASH hHash, DWORD dwKeySpec, LPCSTR sDescription, DWORD dwFlags, BYTE *pbSignature, DWORD *pdwSigLen); typedef BOOL ( WINAPI * CRYPT_SIGN_HASH_FNW)( HCRYPTHASH hHash, DWORD dwKeySpec, LPCWSTR sDescription, DWORD dwFlags, BYTE *pbSignature, DWORD *pdwSigLen); typedef BOOL ( WINAPI * CRYPT_SET_HASH_PARAM_FN)( HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD dwFlags); HINSTANCE g_hAdvapi32 = NULL; CRYPT_CREATE_HASH_FN g_CryptCreateHash = NULL; CRYPT_HASH_DATA_FN g_CryptHashData = NULL; CRYPT_DESTROY_HASH_FN g_CryptDestroyHash = NULL; CRYPT_SIGN_HASH_FNA g_CryptSignHashA = NULL; CRYPT_SIGN_HASH_FNW g_CryptSignHashW = NULL; CRYPT_SET_HASH_PARAM_FN g_CryptSetHashParam = NULL; SignatureSystem sigRSAMD2 = { SP_SIG_RSA_MD2, SigRSAMD2Sign, SigRSAMD2Verify}; SignatureSystem sigRSAMD5 = { SP_SIG_RSA_MD5, SigRSAMD5Sign, SigRSAMD5Verify}; SignatureSystem sigRSASHAMD5 = { SP_SIG_RSA_SHAMD5, SigRSASHAMD5Sign, SigRSASHAMD5Verify}; BOOL WINAPI capiCryptCreateHash( HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash) { if(!g_hAdvapi32) { g_hAdvapi32 = LoadLibrary(ADVAPI32_PATH); if(!g_hAdvapi32) { return FALSE; } } if(!g_CryptCreateHash) { g_CryptCreateHash = (CRYPT_CREATE_HASH_FN)GetProcAddress(g_hAdvapi32, CRYPT_CREATE_HASH_NAME); if(!g_CryptCreateHash) { return FALSE; } } return g_CryptCreateHash(hProv, Algid, hKey, dwFlags, phHash); } BOOL WINAPI capiCryptHashData( HCRYPTHASH hHash, CONST BYTE *pbData, DWORD dwDataLen, DWORD dwFlags) { if(!g_hAdvapi32) { g_hAdvapi32 = LoadLibrary(ADVAPI32_PATH); if(!g_hAdvapi32) { return FALSE; } } if(!g_CryptHashData) { g_CryptHashData = (CRYPT_HASH_DATA_FN)GetProcAddress(g_hAdvapi32, CRYPT_HASH_DATA_NAME); if(!g_CryptHashData) { return FALSE; } } return g_CryptHashData(hHash, pbData, dwDataLen, dwFlags); } BOOL WINAPI capiCryptDestroyHash( HCRYPTHASH hHash) { if(!g_hAdvapi32) { g_hAdvapi32 = LoadLibrary(ADVAPI32_PATH); if(!g_hAdvapi32) { return FALSE; } } if(!g_CryptDestroyHash) { g_CryptDestroyHash = (CRYPT_DESTROY_HASH_FN)GetProcAddress(g_hAdvapi32, CRYPT_DESTROY_HASH_NAME); if(!g_CryptDestroyHash) { return FALSE; } } return g_CryptDestroyHash(hHash); } BOOL WINAPI capiCryptSignHashA( HCRYPTHASH hHash, DWORD dwKeySpec, LPCSTR sDescription, DWORD dwFlags, BYTE *pbSignature, DWORD *pdwSigLen) { if(!g_hAdvapi32) { g_hAdvapi32 = LoadLibrary(ADVAPI32_PATH); if(!g_hAdvapi32) { return FALSE; } } if(!g_CryptSignHashA) { g_CryptSignHashA = (CRYPT_SIGN_HASH_FNA)GetProcAddress(g_hAdvapi32, CRYPT_SIGN_HASH_NAMEA); if(!g_CryptSignHashA) { return FALSE; } } return g_CryptSignHashA(hHash, dwKeySpec, sDescription, dwFlags, pbSignature, pdwSigLen); } BOOL WINAPI capiCryptSignHashW( HCRYPTHASH hHash, DWORD dwKeySpec, LPCWSTR sDescription, DWORD dwFlags, BYTE *pbSignature, DWORD *pdwSigLen) { if(!g_hAdvapi32) { g_hAdvapi32 = LoadLibrary(ADVAPI32_PATH); if(!g_hAdvapi32) { return FALSE; } } if(!g_CryptSignHashW) { g_CryptSignHashW = (CRYPT_SIGN_HASH_FNW)GetProcAddress(g_hAdvapi32, CRYPT_SIGN_HASH_NAMEW); if(!g_CryptSignHashW) { return FALSE; } } return g_CryptSignHashW(hHash, dwKeySpec, sDescription, dwFlags, pbSignature, pdwSigLen); } BOOL WINAPI capiCryptSetHashParam( HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD dwFlags) { if(!g_hAdvapi32) { g_hAdvapi32 = LoadLibrary(ADVAPI32_PATH); if(!g_hAdvapi32) { return FALSE; } } if(!g_CryptSetHashParam) { g_CryptSetHashParam = (CRYPT_SET_HASH_PARAM_FN)GetProcAddress(g_hAdvapi32, CRYPT_SET_HASH_PARAM_NAME); if(!g_CryptSetHashParam) { return FALSE; } } return g_CryptSetHashParam(hHash, dwParam, pbData, dwFlags); } #ifdef UNICODE #define capiCryptSignHash capiCryptSignHashW #else #define capiCryptSignHash capiCryptSignHashA #endif // !UNICODE BOOL WINAPI SigRSAMD5Sign( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD *pcbSigned, PctPrivateKey *pKey) { MD5_CTX DigCtx; BSAFE_PRV_KEY *pk = (BSAFE_PRV_KEY *)pKey->pKey; UCHAR LocalBuffer[300]; UCHAR LocalOutput[300]; unsigned int cbSize; if(pk->magic != RSA2 && pKey->cbKey == sizeof(HCRYPTPROV)) { // This isn't a bsafe key, and it's the right size, so it must be a // CAPI key. This a heuristic. HCRYPTHASH hHash; DWORD cbSigned; HCRYPTPROV hProv = *((HCRYPTPROV *)pKey->pKey); if(!capiCryptCreateHash(hProv, CALG_MD5, 0, 0, &hHash)) { return FALSE; } if(!capiCryptHashData(hHash, pData, cbData, 0)) { capiCryptDestroyHash(hHash); return FALSE; } cbSigned = sizeof(LocalOutput); if(!capiCryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, LocalOutput, &cbSigned)) { capiCryptDestroyHash(hHash); return FALSE; } capiCryptDestroyHash(hHash); ReverseMemCopy(pSigned, LocalOutput, cbSigned); *pcbSigned = cbSigned; return TRUE; } else if(pk->magic != RSA2) { // This isn't a bsafe key or a CAPI key, so it must be a WinSock 2 // LSP key. SSLSIGNATUREFUNC pSignHook; LPVOID pSignArg; /* Generate the checksum */ MD5Init(&DigCtx); MD5Update(&DigCtx, pData, cbData); MD5Final(&DigCtx); // Get the prelude data and the hash value. CopyMemory(LocalBuffer, MD5_PRELUDE, sizeof(MD5_PRELUDE)); CopyMemory(LocalBuffer + sizeof(MD5_PRELUDE), DigCtx.digest, MD5DIGESTLEN); // Get pointer to callback function. pSignHook = ((PSCH_CRED_SECRET_WINSOCK2)pKey->pKey)->pSignatureHookFunc; pSignArg = ((PSCH_CRED_SECRET_WINSOCK2)pKey->pKey)->pSignatureHookArg; // Invoke the callback function. if(pSignHook) { if(pSignHook(SSL_SIGN_RSA, pSignArg, LocalBuffer, sizeof(MD5_PRELUDE) + MD5DIGESTLEN, 0, pSigned, pcbSigned) != SSL_ERR_OKAY) { return FALSE; } } else { DebugLog((DEB_ERROR, "Null signature callback function!\n")); } // Return success. return TRUE; } cbSize = sizeof(MD5_PRELUDE)+16; if(pk->datalen > sizeof(LocalBuffer)) { return FALSE; } /* Generate the checksum */ MD5Init(&DigCtx); MD5Update(&DigCtx, pData, cbData); MD5Final(&DigCtx); FillMemory(LocalBuffer, pk->keylen, 0); ReverseMemCopy(LocalBuffer, DigCtx.digest, 16); ReverseMemCopy(LocalBuffer+16, MD5_PRELUDE, sizeof(MD5_PRELUDE)); LocalBuffer[cbSize++]=0; while(cbSize < pk->datalen-1) { LocalBuffer[cbSize++] = 0xff; } /* Make into pkcs block type 1 */ LocalBuffer[pk->datalen-1] = 1; *pcbSigned = pk->datalen+1; if(!BSafeDecPrivate(pk, LocalBuffer, LocalOutput)) { return FALSE; } ReverseMemCopy(pSigned, LocalOutput, *pcbSigned); return TRUE; } BOOL WINAPI SigRSAMD2Sign( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD *pcbSigned, PctPrivateKey *pKey) { MD2_CTX DigCtx; BSAFE_PRV_KEY *pk = (BSAFE_PRV_KEY *)pKey->pKey; UCHAR LocalBuffer[500]; UCHAR LocalOutput[500]; unsigned int cbSize; if(pk->magic != RSA2) { // This is not a bsafe key, so it must be a CAPI // key. HCRYPTHASH hHash; DWORD cbSigned; HCRYPTPROV hProv = *((HCRYPTPROV *)pKey->pKey); if(!capiCryptCreateHash(hProv, CALG_MD2, 0, 0, &hHash)) { return FALSE; } if(!capiCryptHashData(hHash, pData, cbData, 0)) { capiCryptDestroyHash(hHash); return FALSE; } cbSigned = sizeof(LocalOutput); if(!capiCryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, LocalOutput, &cbSigned)) { capiCryptDestroyHash(hHash); return FALSE; } capiCryptDestroyHash(hHash); ReverseMemCopy(pSigned, LocalOutput, cbSigned); *pcbSigned = cbSigned; return TRUE; } cbSize = sizeof(MD2_PRELUDE)+16; if(pk->datalen > sizeof(LocalBuffer)) { return FALSE; } //MD2Init(&DigCtx); FillMemory( &DigCtx, sizeof( DigCtx ), 0 ); MD2Update(&DigCtx, pData, cbData); MD2Final(&DigCtx); FillMemory(LocalBuffer, pk->keylen, 0); ReverseMemCopy(LocalBuffer, DigCtx.state, 16); ReverseMemCopy(LocalBuffer+16, MD2_PRELUDE, sizeof(MD2_PRELUDE)); LocalBuffer[cbSize++]=0; while(cbSize < pk->datalen-1) { LocalBuffer[cbSize++] = 0xff; } /* Make into pkcs block type 1 */ LocalBuffer[pk->datalen-1] = 1; *pcbSigned = pk->datalen+1; if(!BSafeDecPrivate(pk, LocalBuffer, LocalOutput)) { return FALSE; } ReverseMemCopy(pSigned, LocalOutput, *pcbSigned); return TRUE; } BOOL WINAPI SigRSAMD5Verify( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD cbSigned, PctPublicKey *pKey) { MD5_CTX DigCtx; BSAFE_PUB_KEY *pk = (BSAFE_PUB_KEY *)pKey->pKey; UCHAR Buffer[500]; UCHAR SigBuffer[500]; DWORD iLoc; if(pk->datalen > sizeof(Buffer) || cbSigned != pk->datalen+1) { return FALSE; } MD5Init(&DigCtx); MD5Update(&DigCtx, pData, cbData); MD5Final(&DigCtx); FillMemory(SigBuffer, pk->keylen, 0); FillMemory(Buffer, pk->keylen, 0); ReverseMemCopy(SigBuffer, pSigned, cbSigned); if(!BSafeEncPublic(pk, SigBuffer, Buffer)) { return FALSE; } ReverseMemCopy(SigBuffer, Buffer, cbSigned); /* Make sure pkcs block type 1 */ if(SigBuffer[0] != 0 || SigBuffer[1] != 1) { return FALSE; } for(iLoc = 2; iLoc < pk->datalen; iLoc++ ){ if(!SigBuffer[iLoc]) { break; } if(SigBuffer[iLoc] != 0xff) { return FALSE; } } if(iLoc == pk->datalen) return FALSE; iLoc++; /* skip past separator */ if(memcmp(&SigBuffer[iLoc], MD5_PRELUDE, sizeof(MD5_PRELUDE)) != 0) { return FALSE; } iLoc += sizeof(MD5_PRELUDE); if(memcmp(&SigBuffer[iLoc], DigCtx.digest, 16) != 0) { return FALSE; } return TRUE; } BOOL WINAPI SigRSAMD2Verify( PUCHAR pData, DWORD cbData, PUCHAR pSigned, DWORD cbSigned, PctPublicKey *pKey) { MD2_CTX DigCtx; BSAFE_PUB_KEY *pk = (BSAFE_PUB_KEY *)pKey->pKey; UCHAR Buffer[500]; UCHAR SigBuffer[500]; DWORD iLoc; if ((pk->datalen > sizeof(Buffer)) || (cbSigned != pk->datalen + 1)) { return FALSE; } // MD2Init(&DigCtx); FillMemory( &DigCtx, sizeof( DigCtx ), 0 ); MD2Update(&DigCtx, pData, cbData); MD2Final(&DigCtx); FillMemory(SigBuffer, pk->keylen, 0); FillMemory(Buffer, pk->keylen, 0); ReverseMemCopy(SigBuffer, pSigned, cbSigned); if(!BSafeEncPublic(pk, SigBuffer, Buffer)) { return FALSE; } ReverseMemCopy(SigBuffer, Buffer, cbSigned); /* Make sure pkcs block type 1 */ if(SigBuffer[0] != 0 || SigBuffer[1] != 1) { return FALSE; } for(iLoc = 2; iLoc < pk->datalen; iLoc++ ) { if(!SigBuffer[iLoc]) { break; } if(SigBuffer[iLoc] != 0xff) { return FALSE; } } if(iLoc == pk->datalen) { return FALSE; } iLoc++; /* skip past separator */ if(memcmp(&SigBuffer[iLoc], MD2_PRELUDE, sizeof(MD2_PRELUDE)) != 0) { return FALSE; } iLoc += sizeof(MD2_PRELUDE); if(memcmp(&SigBuffer[iLoc], DigCtx.state, 16) != 0) { return FALSE; } return TRUE; } BOOL WINAPI SigRSASHAMD5Sign( PUCHAR pData, // pointer to hash value DWORD cbData, // always 36 PUCHAR pSigned, DWORD *pcbSigned, PctPrivateKey *pKey) { BSAFE_PRV_KEY *pk = (BSAFE_PRV_KEY *)pKey->pKey; if(pk->magic == RSA2) { // BSAFE key UCHAR LocalBuffer[500]; UCHAR LocalOutput[500]; pk = (BSAFE_PRV_KEY *)pKey; if(pk->keylen > sizeof(LocalBuffer)) { return FALSE; } FillMemory(LocalBuffer, pk->keylen, 0); ReverseMemCopy(LocalBuffer, pData, cbData); LocalBuffer[cbData++] = 0; while(cbData < pk->datalen-1) { LocalBuffer[cbData++] = 0xff; } /* Make into pkcs block type 1 */ LocalBuffer[pk->datalen-1] = 1; *pcbSigned = pk->datalen+1; BSafeDecPrivate(pk, LocalBuffer, LocalOutput); ReverseMemCopy(pSigned, LocalOutput, *pcbSigned); return TRUE; } else { // capiCryptoAPI key HCRYPTPROV hProv; HCRYPTHASH hHash; DWORD dwAlgid; DWORD i; DWORD dwT; // get handle to CSP hProv = *((HCRYPTPROV *)pKey->pKey); // create hash object dwAlgid = ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5; if(RCRYPT_FAILED(capiCryptCreateHash(hProv, dwAlgid, 0, 0, &hHash))) { return FALSE; } // set hash value if(RCRYPT_FAILED(capiCryptSetHashParam(hHash, HP_HASHVAL, pData, 0))) { capiCryptDestroyHash(hHash); return FALSE; } // sign hash if(RCRYPT_FAILED(capiCryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, pSigned, pcbSigned))) { capiCryptDestroyHash(hHash); return FALSE; } // free hash object capiCryptDestroyHash(hHash); //Convert to Big-endian dwT = *pcbSigned; for( i = 0 ; i < dwT/2 ; i++) { BYTE bT = pSigned[i]; pSigned[i] = pSigned[dwT-1-i]; pSigned[dwT-1-i] = bT; } return TRUE; } } BOOL WINAPI SigRSASHAMD5Verify( PUCHAR pData, // pointer to hash value DWORD cbData, // always 36 PUCHAR pSigned, DWORD cbSigned, PctPublicKey *pKey) { BSAFE_PUB_KEY *pk = (BSAFE_PUB_KEY *)pKey->pKey; UCHAR Buffer[500]; UCHAR SigBuffer[500]; DWORD iLoc; if(pk->keylen > sizeof(Buffer) || cbSigned != pk->datalen + 1) { return FALSE; } FillMemory(SigBuffer, pk->keylen, 0); FillMemory(Buffer, pk->keylen, 0); ReverseMemCopy(SigBuffer, pSigned, cbSigned); BSafeEncPublic(pk, SigBuffer, Buffer); ReverseMemCopy(SigBuffer, Buffer, cbSigned); /* Make sure pkcs block type 1 */ if(SigBuffer[0] != 0 || SigBuffer[1] != 1) return FALSE; for(iLoc = 2; iLoc < pk->datalen; iLoc++ ){ if(!SigBuffer[iLoc]) break; if(SigBuffer[iLoc] != 0xff) return FALSE; } if(iLoc == pk->datalen) return FALSE; iLoc++; /* skip past separator */ if(memcmp(&SigBuffer[iLoc], pData, cbData) != 0) return FALSE; return TRUE; }