Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

721 lines
21 KiB

/////////////////////////////////////////////////////////////////////////////
// FILE : nt_sign.c //
// DESCRIPTION : Crypto CP interfaces: //
// CPSignHash //
// CPVerifySignature //
// AUTHOR : //
// HISTORY : //
// Jan 25 1995 larrys Changed from Nametag //
// Feb 23 1995 larrys Changed NTag_SetLastError to SetLastError //
// Mar 23 1995 larrys Added variable key length //
// May 10 1995 larrys added private api calls //
// Aug 03 1995 larrys Fix for bug 10 //
// Aug 22 1995 larrys Added descriptions to sign and verify hash //
// Aug 30 1995 larrys Changed Algid to dwKeySpec //
// Aug 30 1995 larrys Removed RETURNASHVALUE from CryptGetHashValue //
// Aug 31 1995 larrys Fixed CryptSignHash for pbSignature == NULL //
// Aug 31 1995 larrys Fix for Bug 28 //
// Sep 12 1995 Jeffspel/ramas Merged STT onto SCP //
// Sep 12 1995 Jeffspel/ramas BUGS FIXED PKCS#1 Padding. //
// Sep 18 1995 larrys Removed flag fro CryptSignHash //
// Oct 13 1995 larrys Changed GetHashValue to GetHashParam //
// Oct 23 1995 larrys Added MD2 //
// Oct 25 1995 larrys Change length of sDescription string //
// Nov 10 1995 DBarlow Bug #61 //
// Dec 11 1995 larrys Added error return check //
// May 15 1996 larrys Changed NTE_NO_MEMORY to ERROR_NOT_ENOUGHT... //
// May 29 1996 larrys Bug 101 //
// Jun 6 1996 a-johnb Added support for SSL 3.0 signatures //
// //
// Copyright (C) 1993 Microsoft Corporation All Rights Reserved //
/////////////////////////////////////////////////////////////////////////////
#include <wtypes.h>
#include "precomp.h"
#include "nt_rsa.h"
//
// Reverse ASN.1 Encodings of possible hash identifiers. The leading byte is the length
// of the byte string.
//
static const BYTE
#ifdef CSP_USE_MD2
*md2Encodings[]
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
= {
"\x12\x10\x04\x00\x05\x02\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0c\x30\x20\x30",
"\x10\x10\x04\x02\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0a\x30\x1e\x30",
"\x00" },
#endif
#ifdef CSP_USE_MD4
*md4Encodings[]
= {
"\x12\x10\x04\x00\x05\x04\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0c\x30\x20\x30",
"\x10\x10\x04\x04\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0a\x30\x1e\x30",
"\x00" },
#endif
#ifdef CSP_USE_MD5
*md5Encodings[]
= {
"\x12\x10\x04\x00\x05\x05\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0c\x30\x20\x30",
"\x10\x10\x04\x05\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0a\x30\x1e\x30",
"\x00" },
#endif
#ifdef CSP_USE_SHA
*shaEncodings[]
= {
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
"\x0f\x14\x04\x00\x05\x1a\x02\x03\x0e\x2b\x05\x06\x09\x30\x21\x30",
"\x0d\x14\x04\x1a\x02\x03\x0e\x2b\x05\x06\x07\x30\x1f\x30",
"\x00" },
#endif
*endEncodings[]
= { "\x00" };
/*
- CPSignHash
-
* Purpose:
* Create a digital signature from a hash
*
*
* Parameters:
* IN hUID - Handle to the user identifcation
* IN hHash - Handle to hash object
* IN dwKeySpec - Key pair that is used to sign with
* algorithm to be used
* IN sDescription - Description of data to be signed
* IN dwFlags - Flags values
* OUT pbSignture - Pointer to signature data
* OUT dwHashLen - Pointer to the len of the signature data
*
* Returns:
*/
BOOL CPSignHash(IN HCRYPTPROV hUID,
IN HCRYPTHASH hHash,
IN DWORD dwKeySpec,
IN LPCWSTR sDescription,
IN DWORD dwFlags,
OUT BYTE *pbSignature,
OUT DWORD *pdwSigLen)
{
PNTAGUserList pTmpUser;
PNTAGHashList pTmpHash;
BSAFE_PRV_KEY *pPrivKey;
MD2_object *pMD2Hash;
MD4_object *pMD4Hash;
MD5_object *pMD5Hash;
A_SHA_CTX *pSHAHash;
DWORD dwTmp;
BYTE *pInput;
BYTE *pbSigT;
LPBYTE pbStart, pbEnd;
BYTE bTmp;
if (dwFlags != 0)
{
SetLastError((DWORD) NTE_BAD_FLAGS);
return NTF_FAILED;
}
if ((pTmpUser = (PNTAGUserList) NTLCheckList (hUID, USER_HANDLE)) == NULL)
{
SetLastError((DWORD) NTE_BAD_UID);
return NTF_FAILED;
}
if (pTmpUser->Rights & CRYPT_VERIFYCONTEXT)
{
SetLastError((DWORD) NTE_PERM);
return NTF_FAILED;
}
if (CryptConfirmSignature(pTmpUser->hPrivuid,
(CHAR *) sDescription) == CPPAPI_FAILED)
{
return NTF_FAILED;
}
// get the user's private key
if (dwKeySpec == AT_KEYEXCHANGE)
{
#ifdef STT
ASSERT(FALSE);
#endif
pPrivKey = (BSAFE_PRV_KEY *)pTmpUser->pExchPrivKey;
}
else if (dwKeySpec == AT_SIGNATURE)
{
pPrivKey = (BSAFE_PRV_KEY *)pTmpUser->pSigPrivKey;
}
else
{
SetLastError((DWORD) NTE_BAD_FLAGS);
return NTF_FAILED;
}
if (pPrivKey == NULL)
{
SetLastError((DWORD) NTE_NO_KEY);
return NTF_FAILED;
}
if (pbSignature == NULL || *pdwSigLen < (pPrivKey->keylen-(2*sizeof(DWORD))))
{
*pdwSigLen = pPrivKey->keylen- 2*sizeof(DWORD);
if (pbSignature == NULL)
{
return NTF_SUCCEED;
}
SetLastError(ERROR_MORE_DATA);
return NTF_FAILED;
}
if ((pInput = (BYTE *)_nt_malloc(pPrivKey->keylen)) == NULL)
{
_nt_free (pInput, pPrivKey->keylen);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NTF_FAILED;
}
// check to see if the hash is in the hash list
if ((pTmpHash = (PNTAGHashList)NTLValidate(hHash, hUID,
HASH_HANDLE)) == NULL)
{
if (GetLastError() == NTF_FAILED)
{
SetLastError((DWORD) NTE_BAD_HASH);
}
_nt_free (pInput, pPrivKey->keylen);
return NTF_FAILED;
}
// initialize the input buffer to PKCS format.
memset(pInput, 0x00, pPrivKey->keylen);
// insert the block type
pInput[pPrivKey->datalen - 1] = 0x01;
// insert the type I padding
memset(pInput, 0xff, pPrivKey->datalen-1);
// zero the output buffer
memset (pbSignature, 0, pPrivKey->keylen-(2*sizeof(DWORD)));
switch (pTmpHash->Algid)
{
#ifdef CSP_USE_MD2
case CALG_MD2:
pMD2Hash = (MD2_object *)pTmpHash->pHashData;
if (pMD2Hash->FinishFlag == TRUE)
{
_nt_free(pInput, pPrivKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH_STATE);
return NTF_FAILED;
}
break;
#endif
#ifdef CSP_USE_MD4
case CALG_MD4:
pMD4Hash = (MD4_object *)pTmpHash->pHashData;
if (pMD4Hash->FinishFlag == TRUE)
{
_nt_free(pInput, pPrivKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH_STATE);
return NTF_FAILED;
}
break;
#endif
#ifdef CSP_USE_MD5
case CALG_MD5:
pMD5Hash = (MD5_object *)pTmpHash->pHashData;
if (pMD5Hash->FinishFlag == TRUE)
{
_nt_free(pInput, pPrivKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH_STATE);
return NTF_FAILED;
}
break;
#endif
#ifdef CSP_USE_SHA
case CALG_SHA:
{
pSHAHash = (A_SHA_CTX *)pTmpHash->pHashData;
if (pSHAHash->FinishFlag == TRUE)
{
_nt_free(pInput, pPrivKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH_STATE);
return NTF_FAILED;
}
break;
}
#endif
#ifdef CSP_USE_SSL3SHAMD5
case CALG_SSL3_SHAMD5:
// Hash value must have already been set.
if ((pTmpHash->HashFlags & HF_VALUE_SET) == 0)
{
_nt_free(pInput, pPrivKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH_STATE);
return NTF_FAILED;
}
break;
#endif
default:
SetLastError((DWORD) NTE_BAD_ALGID);
_nt_free (pInput, pPrivKey->keylen);
return NTF_FAILED;
}
if (sDescription != NULL)
{
if (!CPHashData(hUID, hHash, (CHAR *) sDescription,
lstrlenW(sDescription) * sizeof(WCHAR), 0))
{
SetLastError((DWORD) NTE_BAD_HASH);
_nt_free(pInput, pPrivKey->keylen);
return NTF_FAILED;
}
}
// now copy the hash into the input buffer
dwTmp = pPrivKey->keylen;
if (!CPGetHashParam(hUID, hHash, HP_HASHVAL, pInput, &dwTmp, 0))
{
_nt_free(pInput, pPrivKey->keylen);
return NTF_FAILED;
}
// Reverse it.
pbStart = pInput;
pbEnd = pbStart + dwTmp - 1;
while (pbStart < pbEnd)
{
bTmp = *pbStart;
*pbStart++ = *pbEnd;
*pbEnd-- = bTmp;
}
switch (pTmpHash->Algid)
{
#ifdef CSP_USE_MD2
case CALG_MD2:
{
// PKCS delimit the hash value
pbEnd = (LPBYTE)md2Encodings[0];
pbStart = pInput + dwTmp;
bTmp = *pbEnd++;
while (0 < bTmp--)
*pbStart++ = *pbEnd++;
*pbStart++ = 0;
break;
}
#endif
#ifdef CSP_USE_MD4
case CALG_MD4:
{
// PKCS delimit the hash value
pbEnd = (LPBYTE)md4Encodings[0];
pbStart = pInput + dwTmp;
bTmp = *pbEnd++;
while (0 < bTmp--)
*pbStart++ = *pbEnd++;
*pbStart++ = 0;
break;
}
#endif
#ifdef CSP_USE_MD5
case CALG_MD5:
{
// PKCS delimit the hash value
pbEnd = (LPBYTE)md5Encodings[0];
pbStart = pInput + dwTmp;
bTmp = *pbEnd++;
while (0 < bTmp--)
*pbStart++ = *pbEnd++;
*pbStart++ = 0;
break;
}
#endif
#ifdef CSP_USE_SHA
case CALG_SHA:
{
// PKCS delimit the hash value
pbEnd = (LPBYTE)shaEncodings[0];
pbStart = pInput + dwTmp;
bTmp = *pbEnd++;
while (0 < bTmp--)
*pbStart++ = *pbEnd++;
*pbStart++ = 0;
break;
}
#endif
#ifdef CSP_USE_SSL3SHAMD5
case CALG_SSL3_SHAMD5:
// Don't put in any PKCS crud
pbStart = pInput + dwTmp;
*pbStart++ = 0;
break;
#endif
default:
SetLastError((DWORD) NTE_BAD_ALGID);
_nt_free (pInput, pPrivKey->keylen);
return NTF_FAILED;
}
// encrypt the hash value in input
if (NULL == (pbSigT = (BYTE *)_nt_malloc(pPrivKey->keylen)))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
_nt_free (pInput, pPrivKey->keylen);
return NTF_FAILED;
}
memset(pbSigT, 0, pPrivKey->keylen);
BSafeDecPrivate(pPrivKey, pInput, pbSigT);
memcpy(pbSignature, pbSigT, pPrivKey->keylen-2*sizeof(DWORD));
_nt_free (pbSigT, pPrivKey->keylen);
*pdwSigLen = pPrivKey->keylen-2*sizeof(DWORD);
_nt_free (pInput, pPrivKey->keylen);
return NTF_SUCCEED;
}
/*
- CPVerifySignature
-
* Purpose:
* Used to verify a signature against a hash object
*
*
* Parameters:
* IN hUID - Handle to the user identifcation
* IN hHash - Handle to hash object
* IN pbSignture - Pointer to signature data
* IN dwSigLen - Length of the signature data
* IN hPubKey - Handle to the public key for verifying
* the signature
* IN Algid - Algorithm identifier of the signature
* algorithm to be used
* IN sDescription - String describing the signed data
* IN dwFlags - Flags values
*
* Returns:
*/
BOOL CPVerifySignature(IN HCRYPTPROV hUID,
IN HCRYPTHASH hHash,
IN CONST BYTE *pbSignature,
IN DWORD dwSigLen,
IN HCRYPTKEY hPubKey,
IN LPCWSTR sDescription,
IN DWORD dwFlags)
{
PNTAGUserList pTmpUser;
PNTAGHashList pTmpHash;
PNTAGKeyList pPubKey;
BSAFE_PUB_KEY *pKey;
BYTE *pOutput;
BYTE *pTmp;
BSAFE_PUB_KEY *pBsafePubKey;
MD2_object *pMD2Hash;
MD4_object *pMD4Hash;
MD5_object *pMD5Hash;
A_SHA_CTX *pSHAHash;
long size;
long temp;
BYTE *pbTmp;
BYTE *pbSigT;
int i;
const BYTE **rgEncOptions;
LPBYTE pbStart, pbEnd;
BYTE bTmp;
if (dwFlags != 0)
{
SetLastError((DWORD) NTE_BAD_FLAGS);
return NTF_FAILED;
}
// check the user identification
if ((pTmpUser = (PNTAGUserList) NTLCheckList (hUID, USER_HANDLE)) == NULL)
{
SetLastError((DWORD) NTE_BAD_UID);
return NTF_FAILED;
}
// check to see if the hash is in the hash list
if ((pTmpHash = (PNTAGHashList) NTLValidate(hHash, hUID,
HASH_HANDLE)) == NULL)
{
// NTLValidate doesn't know what error to set
// so it set NTE_FAIL -- fix it up.
if (GetLastError() == NTE_FAIL)
SetLastError((DWORD) NTE_BAD_HASH);
return NTF_FAILED;
}
switch(HNTAG_TO_HTYPE((HNTAG)hPubKey))
{
case SIGPUBKEY_HANDLE:
case EXCHPUBKEY_HANDLE:
#ifdef STT
ASSERT(HNTAG_TO_HTYPE((HNTAG)hPubKey) != EXCHPUBKEY_HANDLE);
#endif
if ((pPubKey = (PNTAGKeyList) NTLValidate((HNTAG)hPubKey,
hUID, HNTAG_TO_HTYPE((HNTAG)hPubKey))) == NULL)
{
// NTLValidate doesn't know what error to set
// so it set NTE_FAIL -- fix it up.
if (GetLastError() == NTE_FAIL)
SetLastError((DWORD) NTE_BAD_KEY);
return NTF_FAILED;
}
break;
default:
SetLastError((DWORD) NTE_BAD_KEY);
return NTF_FAILED;
}
pKey = (BSAFE_PUB_KEY *)pPubKey->pKeyValue;
pBsafePubKey = (BSAFE_PUB_KEY *) pKey;
#ifdef STT
if (dwSigLen != pKey->keylen-2*sizeof(DWORD))
{
SetLastError((DWORD) NTE_BAD_SIGNATURE);
return NTF_FAILED;
}
#endif
if ((pOutput = (BYTE *)_nt_malloc(pBsafePubKey->keylen)) == NULL)
{
_nt_free (pOutput, pBsafePubKey->keylen);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NTF_FAILED;
}
#ifdef STT
memset (pOutput, 0, dwSigLen);
#endif
// encrypt the hash value in output
if (NULL == (pbSigT = (BYTE *)_nt_malloc(pBsafePubKey->keylen)))
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
_nt_free (pOutput, pBsafePubKey->keylen);
return NTF_FAILED;
}
memset(pbSigT, 0, pBsafePubKey->keylen);
memcpy(pbSigT, pbSignature, pBsafePubKey->keylen-2*sizeof(DWORD));
if (BSafeEncPublic(pBsafePubKey, pbSigT, pOutput) == FALSE)
{
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
_nt_free (pOutput, pBsafePubKey->keylen);
return NTF_FAILED;
}
_nt_free (pbSigT, pBsafePubKey->keylen);
switch(pTmpHash->Algid)
{
#ifdef CSP_USE_MD2
case CALG_MD2:
pMD2Hash = (MD2_object *)pTmpHash->pHashData;
if (pMD2Hash->FinishFlag == TRUE)
{
_nt_free(pOutput, pBsafePubKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH);
return NTF_FAILED;
}
pbTmp = (BYTE *) &(pMD2Hash->MD);
size = MD2DIGESTLEN;
rgEncOptions = md2Encodings;
break;
#endif
#ifdef CSP_USE_MD4
case CALG_MD4:
pMD4Hash = (MD4_object *)pTmpHash->pHashData;
if (pMD4Hash->FinishFlag == TRUE)
{
_nt_free(pOutput, pBsafePubKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH);
return NTF_FAILED;
}
pbTmp = (BYTE *) &(pMD4Hash->MD);
size = MD4DIGESTLEN;
rgEncOptions = md4Encodings;
break;
#endif
#ifdef CSP_USE_MD5
case CALG_MD5:
pMD5Hash = (MD5_object *)pTmpHash->pHashData;
pbTmp = (BYTE *)pMD5Hash->digest;
size = MD5DIGESTLEN;
rgEncOptions = md5Encodings;
break;
#endif
#ifdef CSP_USE_SHA
case CALG_SHA:
pSHAHash = (A_SHA_CTX *)pTmpHash->pHashData;
if (pSHAHash->FinishFlag == TRUE)
{
_nt_free(pOutput, pBsafePubKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH);
return NTF_FAILED;
}
pbTmp = (BYTE *)pSHAHash->HashVal;
size = A_SHA_DIGEST_LEN;
rgEncOptions = shaEncodings;
break;
#endif
#ifdef CSP_USE_SSL3SHAMD5
case CALG_SSL3_SHAMD5:
// Hash value must have already been set.
if ((pTmpHash->HashFlags & HF_VALUE_SET) == 0)
{
_nt_free(pOutput, pBsafePubKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH);
return NTF_FAILED;
}
pbTmp = pTmpHash->pHashData;
size = SSL3_SHAMD5_LEN;
rgEncOptions = NULL;
break;
#endif
default:
_nt_free(pOutput, pBsafePubKey->keylen);
SetLastError((DWORD) NTE_BAD_HASH);
return NTF_FAILED;
}
if (sDescription != NULL)
{
if (!CPHashData(hUID, hHash, (CHAR *) sDescription,
lstrlenW(sDescription) * sizeof(WCHAR), 0))
{
_nt_free(pOutput, pBsafePubKey->keylen);
return NTF_FAILED;
}
}
if ((pTmp = (BYTE *)_nt_malloc(size)) == NULL)
{
_nt_free (pOutput, pBsafePubKey->keylen);
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return NTF_FAILED;
}
temp = size;
if (!CPGetHashParam(hUID, hHash, HP_HASHVAL, pTmp, &temp, 0))
{
_nt_free(pOutput, pBsafePubKey->keylen);
_nt_free (pTmp, size);
return NTF_FAILED;
}
// Reverse the hash to match the signature.
pbStart = pTmp;
pbEnd = pbStart + size - 1;
while (pbStart < pbEnd)
{
bTmp = *pbStart;
*pbStart++ = *pbEnd;
*pbEnd-- = bTmp;
}
// See if it matches.
if (memcmp (pTmp, pOutput, size))
{
_nt_free (pOutput, pBsafePubKey->keylen);
_nt_free (pTmp, size);
SetLastError((DWORD) NTE_BAD_SIGNATURE);
return NTF_FAILED;
}
_nt_free (pTmp, size);
// Check for any signature type identifiers
if(rgEncOptions != NULL) {
for (i = 0; 0 != *rgEncOptions[i]; i += 1)
{
pbStart = (LPBYTE)rgEncOptions[i];
temp = *pbStart++;
if (0 == memcmp(&pOutput[size], pbStart, temp))
{
size += temp; // Adjust the end of the hash data.
break;
}
}
}
// check to make sure the rest of the PKCS #1 padding is correct
if ((0x00 != pOutput[size]) || (0x00 != pOutput[pKey->datalen]) ||
(0x1 != pOutput[pKey->datalen - 1]))
{
_nt_free(pOutput, pKey->keylen);
SetLastError((DWORD) NTE_BAD_SIGNATURE);
return NTF_FAILED;
}
for (i=size+1;i<(long)pKey->datalen-1;i++)
{
if (0xff != pOutput[i])
{
_nt_free(pOutput, pKey->keylen);
SetLastError((DWORD) NTE_BAD_SIGNATURE);
return NTF_FAILED;
}
}
_nt_free (pOutput, pBsafePubKey->keylen);
return NTF_SUCCEED;
}