911 lines
25 KiB
C
911 lines
25 KiB
C
/////////////////////////////////////////////////////////////////////////////
|
|
// 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 //
|
|
// May 23 1997 jeffspel Added provider type checking //
|
|
// May 5 2000 dbarlow Clean up error return codes //
|
|
// //
|
|
// Copyright (C) 1993 - 2000, Microsoft Corporation //
|
|
// All Rights Reserved //
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//#include <wtypes.h>
|
|
#include "precomp.h"
|
|
#include "ntagum.h"
|
|
#include "nt_rsa.h"
|
|
#include "protstor.h"
|
|
#include "swnt_pk.h"
|
|
|
|
extern CSP_STRINGS g_Strings;
|
|
|
|
|
|
//
|
|
// Reverse ASN.1 Encodings of possible hash identifiers. The leading byte is
|
|
// the length of the remaining byte string. The lists of possible identifiers
|
|
// is terminated with a '\x00' entry.
|
|
//
|
|
|
|
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
|
|
= { (CONST BYTE *)"\x12\x10\x04\x00\x05\x02\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0c\x30\x20\x30",
|
|
(CONST BYTE *)"\x10\x10\x04\x02\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0a\x30\x1e\x30",
|
|
(CONST BYTE *)"\x00" },
|
|
#endif
|
|
#ifdef CSP_USE_MD4
|
|
*md4Encodings[]
|
|
= { (CONST BYTE *)"\x12\x10\x04\x00\x05\x04\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0c\x30\x20\x30",
|
|
(CONST BYTE *)"\x10\x10\x04\x04\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0a\x30\x1e\x30",
|
|
(CONST BYTE *)"\x00" },
|
|
#endif
|
|
#ifdef CSP_USE_MD5
|
|
*md5Encodings[]
|
|
= { (CONST BYTE *)"\x12\x10\x04\x00\x05\x05\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0c\x30\x20\x30",
|
|
(CONST BYTE *)"\x10\x10\x04\x05\x02\x0d\xf7\x86\x48\x86\x2a\x08\x06\x0a\x30\x1e\x30",
|
|
(CONST BYTE *)"\x00" },
|
|
#endif
|
|
#ifdef CSP_USE_SHA
|
|
*shaEncodings[]
|
|
= { (CONST BYTE *)"\x0f\x14\x04\x00\x05\x1a\x02\x03\x0e\x2b\x05\x06\x09\x30\x21\x30",
|
|
(CONST BYTE *)"\x0d\x14\x04\x1a\x02\x03\x0e\x2b\x05\x06\x07\x30\x1f\x30",
|
|
(CONST BYTE *)"\x00"},
|
|
#endif
|
|
*endEncodings[]
|
|
= { (CONST BYTE *)"\x00" };
|
|
|
|
|
|
/*
|
|
- ApplyPKCS1SigningFormat
|
|
-
|
|
* Purpose:
|
|
* Format a buffer with PKCS 1 for signing
|
|
*
|
|
*/
|
|
|
|
/*static*/ DWORD
|
|
ApplyPKCS1SigningFormat(
|
|
IN BSAFE_PUB_KEY *pPubKey,
|
|
IN ALG_ID HashAlgid,
|
|
IN BYTE *pbHash,
|
|
IN DWORD cbHash,
|
|
IN DWORD dwFlags,
|
|
OUT BYTE *pbPKCS1Format)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
BYTE *pbStart;
|
|
BYTE *pbEnd;
|
|
BYTE bTmp;
|
|
DWORD i;
|
|
|
|
// insert the block type
|
|
pbPKCS1Format[pPubKey->datalen - 1] = 0x01;
|
|
|
|
// insert the type I padding
|
|
memset(pbPKCS1Format, 0xff, pPubKey->datalen-1);
|
|
|
|
// Reverse it
|
|
for (i = 0; i < cbHash; i++)
|
|
pbPKCS1Format[i] = pbHash[cbHash - (i + 1)];
|
|
|
|
if ( 0 == (CRYPT_NOHASHOID & dwFlags))
|
|
{
|
|
switch (HashAlgid)
|
|
{
|
|
#ifdef CSP_USE_MD2
|
|
case CALG_MD2:
|
|
// PKCS delimit the hash value
|
|
pbEnd = (LPBYTE)md2Encodings[0];
|
|
pbStart = pbPKCS1Format + cbHash;
|
|
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 = pbPKCS1Format + cbHash;
|
|
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 = pbPKCS1Format + cbHash;
|
|
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 = pbPKCS1Format + cbHash;
|
|
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 = pbPKCS1Format + cbHash;
|
|
*pbStart++ = 0;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
dwReturn = (DWORD)NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pbPKCS1Format[cbHash] = 0x00;
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
- ApplyX931SigningFormat
|
|
-
|
|
* Purpose:
|
|
* Format a buffer with X.9.31 for signing, assumes
|
|
* the buffer to be formatted is at least the length
|
|
* of the signature (cbSig).
|
|
*
|
|
*/
|
|
|
|
/*static*/ void
|
|
ApplyX931SigningFormat(
|
|
IN DWORD cbSig,
|
|
IN BYTE *pbHash,
|
|
IN DWORD cbHash,
|
|
IN BOOL fDataInHash,
|
|
OUT BYTE *pbFormatted)
|
|
{
|
|
DWORD i;
|
|
|
|
// insert P3
|
|
pbFormatted[0] = 0xcc;
|
|
pbFormatted[1] = 0x33;
|
|
|
|
// Reverse it
|
|
for (i = 0; i < cbHash; i++)
|
|
pbFormatted[i + 2] = pbHash[cbHash - (i + 1)];
|
|
pbFormatted[22] = 0xba;
|
|
|
|
// insert P2
|
|
memset(pbFormatted + 23, 0xbb, cbSig - 24);
|
|
|
|
// insert P1
|
|
if (fDataInHash)
|
|
pbFormatted[cbSig - 1] = 0x6b;
|
|
else
|
|
pbFormatted[cbSig - 1] = 0x4b;
|
|
}
|
|
|
|
|
|
/*
|
|
- 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 WINAPI
|
|
CPSignHash(
|
|
IN HCRYPTPROV hUID,
|
|
IN HCRYPTHASH hHash,
|
|
IN DWORD dwKeySpec,
|
|
IN LPCWSTR sDescription,
|
|
IN DWORD dwFlags,
|
|
OUT BYTE *pbSignature,
|
|
OUT DWORD *pdwSigLen)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
PNTAGUserList pTmpUser = NULL;
|
|
PNTAGHashList pTmpHash;
|
|
BSAFE_PRV_KEY *pPrivKey = NULL;
|
|
MD2_object *pMD2Hash;
|
|
MD4_object *pMD4Hash;
|
|
MD5_object *pMD5Hash;
|
|
A_SHA_CTX *pSHAHash;
|
|
BYTE rgbTmpHash[SSL3_SHAMD5_LEN];
|
|
DWORD cbTmpHash = SSL3_SHAMD5_LEN;
|
|
BYTE *pbInput = NULL;
|
|
BYTE *pbSigT = NULL;
|
|
DWORD cbSigLen;
|
|
BOOL fSigKey;
|
|
BSAFE_PUB_KEY *pPubKey;
|
|
LPWSTR szPrompt;
|
|
BOOL fDataInHash = FALSE;
|
|
BOOL fRet;
|
|
DWORD dwSts;
|
|
|
|
EntryPoint
|
|
if ((dwFlags & ~(CRYPT_NOHASHOID | CRYPT_X931_FORMAT)) != 0)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_FLAGS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
|
|
if (NULL == pTmpUser)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_UID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// get the user's public key
|
|
if (dwKeySpec == AT_KEYEXCHANGE)
|
|
{
|
|
fSigKey = FALSE;
|
|
pPubKey = (BSAFE_PUB_KEY *)pTmpUser->ContInfo.pbExchPub;
|
|
szPrompt = g_Strings.pwszSignWExch;
|
|
}
|
|
else if (dwKeySpec == AT_SIGNATURE)
|
|
{
|
|
if (PROV_RSA_SCHANNEL == pTmpUser->dwProvType)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
fSigKey = TRUE;
|
|
pPubKey = (BSAFE_PUB_KEY *)pTmpUser->ContInfo.pbSigPub;
|
|
szPrompt = g_Strings.pwszSigning;
|
|
}
|
|
else
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// check to make sure the key exists
|
|
if (NULL == pPubKey)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_KEYSET;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
cbSigLen = (pPubKey->bitlen + 7) / 8;
|
|
if (pbSignature == NULL || *pdwSigLen < cbSigLen)
|
|
{
|
|
*pdwSigLen = cbSigLen;
|
|
dwReturn = (pbSignature == NULL) ? ERROR_SUCCESS : ERROR_MORE_DATA;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// check to see if the hash is in the hash list
|
|
dwSts = NTLValidate(hHash, hUID, HASH_HANDLE, &pTmpHash);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = (dwSts == NTF_FAILED) ? (DWORD)NTE_BAD_HASH : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// zero the output buffer
|
|
memset (pbSignature, 0, cbSigLen);
|
|
|
|
switch (pTmpHash->Algid)
|
|
{
|
|
#ifdef CSP_USE_MD2
|
|
case CALG_MD2:
|
|
pMD2Hash = (MD2_object *)pTmpHash->pHashData;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_MD4
|
|
case CALG_MD4:
|
|
pMD4Hash = (MD4_object *)pTmpHash->pHashData;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_MD5
|
|
case CALG_MD5:
|
|
pMD5Hash = (MD5_object *)pTmpHash->pHashData;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_SHA
|
|
case CALG_SHA:
|
|
pSHAHash = (A_SHA_CTX *)pTmpHash->pHashData;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_SSL3SHAMD5
|
|
case CALG_SSL3_SHAMD5:
|
|
// Hash value must have already been set.
|
|
if ((pTmpHash->HashFlags & HF_VALUE_SET) == 0)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_HASH_STATE;
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
dwReturn = (DWORD)NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// WARNING: due to vulnerabilities sDescription field is should NO longer
|
|
// be used
|
|
if (sDescription != NULL)
|
|
{
|
|
if (!CPHashData(hUID, hHash, (LPBYTE) sDescription,
|
|
lstrlenW(sDescription) * sizeof(WCHAR), 0))
|
|
{
|
|
dwReturn = GetLastError(); // NTE_BAD_HASH
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
// check if this is a NULL hash (no data hashed) for X.9.31 format
|
|
if (pTmpHash->dwHashState & DATA_IN_HASH)
|
|
fDataInHash = TRUE;
|
|
|
|
// now copy the hash into the input buffer
|
|
cbTmpHash = pPubKey->keylen;
|
|
if (!CPGetHashParam(hUID, hHash, HP_HASHVAL, rgbTmpHash, &cbTmpHash, 0))
|
|
{
|
|
dwReturn = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pbInput = (BYTE *)_nt_malloc(pPubKey->keylen);
|
|
if (pbInput == NULL)
|
|
{
|
|
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (dwFlags & CRYPT_X931_FORMAT)
|
|
{
|
|
// use X.9.31 padding for FIPS certification
|
|
if (pTmpHash->Algid != CALG_SHA1)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_ALGID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
ApplyX931SigningFormat(cbSigLen,
|
|
rgbTmpHash,
|
|
cbTmpHash,
|
|
fDataInHash,
|
|
pbInput);
|
|
}
|
|
else
|
|
{
|
|
// use PKCS #1 padding
|
|
dwSts = ApplyPKCS1SigningFormat(pPubKey, pTmpHash->Algid, rgbTmpHash,
|
|
cbTmpHash, dwFlags, pbInput);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
// encrypt the hash value in input
|
|
pbSigT = (BYTE *)_nt_malloc(pPubKey->keylen);
|
|
if (NULL == pbSigT)
|
|
{
|
|
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// load the appropriate key if necessary
|
|
dwSts = UnprotectPrivKey(pTmpUser, szPrompt, fSigKey, FALSE);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts; // NTE_BAD_KEYSET
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// get the user's private key
|
|
if (fSigKey)
|
|
pPrivKey = (BSAFE_PRV_KEY *)pTmpUser->pSigPrivKey;
|
|
else
|
|
pPrivKey = (BSAFE_PRV_KEY *)pTmpUser->pExchPrivKey;
|
|
|
|
if (pPrivKey == NULL)
|
|
{
|
|
dwReturn = (DWORD)NTE_NO_KEY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (pPubKey->keylen != pPrivKey->keylen)
|
|
{
|
|
dwReturn = (DWORD)NTE_KEYSET_ENTRY_BAD;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
dwSts = RSAPrivateDecrypt(pTmpUser->pOffloadInfo, pPrivKey, pbInput,
|
|
pbSigT);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
memcpy(pbSignature, pbSigT, cbSigLen);
|
|
*pdwSigLen = cbSigLen;
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
fRet = (ERROR_SUCCESS == dwReturn);
|
|
if (pbSigT)
|
|
_nt_free (pbSigT, pPubKey->keylen);
|
|
if (pbInput)
|
|
_nt_free(pbInput, pPubKey->keylen);
|
|
if (!fRet)
|
|
SetLastError(dwReturn);
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/*
|
|
- VerifyPKCS1SigningFormat
|
|
-
|
|
* Purpose:
|
|
* Check the format on a buffer to make sure the PKCS 1
|
|
* formatting is correct.
|
|
*
|
|
*/
|
|
|
|
/*static*/ DWORD
|
|
VerifyPKCS1SigningFormat(
|
|
IN BSAFE_PUB_KEY *pKey,
|
|
IN PNTAGHashList pTmpHash,
|
|
IN BYTE *pbHash,
|
|
IN DWORD cbHash,
|
|
IN DWORD dwFlags,
|
|
OUT BYTE *pbPKCS1Format)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
MD2_object *pMD2Hash;
|
|
MD4_object *pMD4Hash;
|
|
MD5_object *pMD5Hash;
|
|
A_SHA_CTX *pSHAHash;
|
|
BYTE *pbTmp;
|
|
const BYTE **rgEncOptions;
|
|
BYTE rgbTmpHash[SSL3_SHAMD5_LEN];
|
|
DWORD i;
|
|
DWORD cb;
|
|
BYTE *pbStart;
|
|
DWORD cbTmp;
|
|
|
|
switch (pTmpHash->Algid)
|
|
{
|
|
#ifdef CSP_USE_MD2
|
|
case CALG_MD2:
|
|
pMD2Hash = (MD2_object *)pTmpHash->pHashData;
|
|
pbTmp = (BYTE *) &(pMD2Hash->MD);
|
|
rgEncOptions = md2Encodings;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_MD4
|
|
case CALG_MD4:
|
|
pMD4Hash = (MD4_object *)pTmpHash->pHashData;
|
|
pbTmp = (BYTE *) &(pMD4Hash->MD);
|
|
rgEncOptions = md4Encodings;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_MD5
|
|
case CALG_MD5:
|
|
pMD5Hash = (MD5_object *)pTmpHash->pHashData;
|
|
pbTmp = (BYTE *)pMD5Hash->digest;
|
|
rgEncOptions = md5Encodings;
|
|
break;
|
|
#endif
|
|
|
|
#ifdef CSP_USE_SHA
|
|
case CALG_SHA:
|
|
pSHAHash = (A_SHA_CTX *)pTmpHash->pHashData;
|
|
pbTmp = (BYTE *)pSHAHash->HashVal;
|
|
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)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_HASH;
|
|
goto ErrorExit;
|
|
}
|
|
pbTmp = pTmpHash->pHashData;
|
|
rgEncOptions = NULL;
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
dwReturn = (DWORD)NTE_BAD_HASH;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Reverse the hash to match the signature.
|
|
for (i = 0; i < cbHash; i++)
|
|
rgbTmpHash[i] = pbHash[cbHash - (i + 1)];
|
|
|
|
// See if it matches.
|
|
if (0 != memcmp(rgbTmpHash, pbPKCS1Format, cbHash))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
cb = cbHash;
|
|
if (!(CRYPT_NOHASHOID & dwFlags))
|
|
{
|
|
// Check for any signature type identifiers
|
|
if (rgEncOptions != NULL)
|
|
{
|
|
for (i = 0; 0 != *rgEncOptions[i]; i += 1)
|
|
{
|
|
pbStart = (LPBYTE)rgEncOptions[i];
|
|
cbTmp = *pbStart++;
|
|
if (0 == memcmp(&pbPKCS1Format[cb], pbStart, cbTmp))
|
|
{
|
|
cb += cbTmp; // Adjust the end of the hash data.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// check to make sure the rest of the PKCS #1 padding is correct
|
|
if ((0x00 != pbPKCS1Format[cb])
|
|
|| (0x00 != pbPKCS1Format[pKey->datalen])
|
|
|| (0x1 != pbPKCS1Format[pKey->datalen - 1]))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
for (i = cb + 1; i < (DWORD)pKey->datalen - 1; i++)
|
|
{
|
|
if (0xff != pbPKCS1Format[i])
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
- VerifyX931SigningFormat
|
|
-
|
|
* Purpose:
|
|
* Check the format on a buffer to make sure the X.9.31
|
|
* formatting is correct.
|
|
*
|
|
*/
|
|
|
|
/*static*/ DWORD
|
|
VerifyX931SigningFormat(
|
|
IN BYTE *pbHash,
|
|
IN DWORD cbHash,
|
|
IN BOOL fDataInHash,
|
|
IN BYTE *pbFormatted,
|
|
IN DWORD cbFormatted)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
BYTE rgbTmpHash[SSL3_SHAMD5_LEN];
|
|
DWORD i;
|
|
|
|
// check P3
|
|
if ((0xcc != pbFormatted[0]) || (0x33 != pbFormatted[1]) ||
|
|
(0xba != pbFormatted[cbHash + 2]))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// Reverse the hash to match the signature and check if it matches.
|
|
for (i = 0; i < cbHash; i++)
|
|
rgbTmpHash[i] = pbHash[cbHash - (i + 1)];
|
|
if (0 != memcmp(rgbTmpHash, pbFormatted + 2, cbHash))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// check P2
|
|
for (i = 23; i < (cbFormatted - 24); i++)
|
|
{
|
|
if (0xbb != pbFormatted[i])
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
// check P1
|
|
if (fDataInHash)
|
|
{
|
|
if (0x6b != pbFormatted[cbFormatted - 1])
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (0x4b != pbFormatted[cbFormatted - 1])
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
return dwReturn;
|
|
}
|
|
|
|
|
|
/*
|
|
- 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 WINAPI
|
|
CPVerifySignature(
|
|
IN HCRYPTPROV hUID,
|
|
IN HCRYPTHASH hHash,
|
|
IN CONST BYTE *pbSignature,
|
|
IN DWORD dwSigLen,
|
|
IN HCRYPTKEY hPubKey,
|
|
IN LPCWSTR sDescription,
|
|
IN DWORD dwFlags)
|
|
{
|
|
DWORD dwReturn = ERROR_INTERNAL_ERROR;
|
|
PNTAGUserList pTmpUser;
|
|
PNTAGHashList pTmpHash;
|
|
PNTAGKeyList pPubKey;
|
|
BSAFE_PUB_KEY *pKey;
|
|
BYTE *pOutput = NULL;
|
|
BSAFE_PUB_KEY *pBsafePubKey;
|
|
BYTE *pbSigT = NULL;
|
|
BYTE rgbTmpHash[SSL3_SHAMD5_LEN];
|
|
DWORD cbTmpHash = SSL3_SHAMD5_LEN;
|
|
DWORD cbLocalSigLen;
|
|
BOOL fDataInHash = FALSE;
|
|
BOOL fRet;
|
|
DWORD dwSts;
|
|
|
|
EntryPoint
|
|
if ((dwFlags & ~(CRYPT_NOHASHOID | CRYPT_X931_FORMAT)) != 0)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_FLAGS;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// check the user identification
|
|
pTmpUser = (PNTAGUserList)NTLCheckList(hUID, USER_HANDLE);
|
|
if (NULL == pTmpUser)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_UID;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// check to see if the hash is in the hash list
|
|
dwSts = NTLValidate(hHash, hUID, HASH_HANDLE, &pTmpHash);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
// NTLValidate doesn't know what error to set
|
|
// so it set NTE_FAIL -- fix it up.
|
|
dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_HASH : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((CALG_MD2 != pTmpHash->Algid) &&
|
|
(CALG_MD4 != pTmpHash->Algid) &&
|
|
(CALG_MD5 != pTmpHash->Algid) &&
|
|
(CALG_SHA != pTmpHash->Algid) &&
|
|
(CALG_SSL3_SHAMD5 != pTmpHash->Algid))
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_HASH;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
switch (HNTAG_TO_HTYPE((HNTAG)hPubKey))
|
|
{
|
|
case SIGPUBKEY_HANDLE:
|
|
case EXCHPUBKEY_HANDLE:
|
|
dwSts = NTLValidate((HNTAG)hPubKey,
|
|
hUID, HNTAG_TO_HTYPE((HNTAG)hPubKey),
|
|
&pPubKey);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
// NTLValidate doesn't know what error to set
|
|
// so it set NTE_FAIL -- fix it up.
|
|
dwReturn = (dwSts == NTE_FAIL) ? (DWORD)NTE_BAD_KEY : dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
break;
|
|
default:
|
|
dwReturn = (DWORD)NTE_BAD_KEY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pKey = (BSAFE_PUB_KEY *)pPubKey->pKeyValue;
|
|
pBsafePubKey = (BSAFE_PUB_KEY *) pKey;
|
|
cbLocalSigLen = (pBsafePubKey->bitlen+7)/8;
|
|
if (dwSigLen != cbLocalSigLen)
|
|
{
|
|
dwReturn = (DWORD)NTE_BAD_SIGNATURE;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
pOutput = (BYTE *)_nt_malloc(pBsafePubKey->keylen);
|
|
if (NULL == pOutput)
|
|
{
|
|
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// encrypt the hash value in output
|
|
pbSigT = (BYTE *)_nt_malloc(pBsafePubKey->keylen);
|
|
if (NULL == pbSigT)
|
|
{
|
|
dwReturn = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
memset(pbSigT, 0, pBsafePubKey->keylen);
|
|
memcpy(pbSigT, pbSignature, cbLocalSigLen);
|
|
dwSts = RSAPublicEncrypt(pTmpUser->pOffloadInfo, pBsafePubKey,
|
|
pbSigT, pOutput);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts; // NTE_BAD_SIGNATURE
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// WARNING: due to vulnerabilities sDescription field is should NO longer
|
|
// be used
|
|
if (sDescription != NULL)
|
|
{
|
|
if (!CPHashData(hUID, hHash, (LPBYTE) sDescription,
|
|
lstrlenW(sDescription) * sizeof(WCHAR), 0))
|
|
{
|
|
dwReturn = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
// check if this is a NULL hash (no data hashed) for X.9.31 format
|
|
if (pTmpHash->dwHashState & DATA_IN_HASH)
|
|
fDataInHash = TRUE;
|
|
|
|
if (!CPGetHashParam(hUID, hHash, HP_HASHVAL, rgbTmpHash, &cbTmpHash, 0))
|
|
{
|
|
dwReturn = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (dwFlags & CRYPT_X931_FORMAT)
|
|
{
|
|
// use X.9.31 padding for FIPS certification
|
|
dwSts = VerifyX931SigningFormat(rgbTmpHash, cbTmpHash,
|
|
fDataInHash, pOutput,
|
|
(pBsafePubKey->bitlen + 7) / 8);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// use PKCS #1 padding
|
|
dwSts = VerifyPKCS1SigningFormat(pKey, pTmpHash, rgbTmpHash,
|
|
cbTmpHash, dwFlags, pOutput);
|
|
if (ERROR_SUCCESS != dwSts)
|
|
{
|
|
dwReturn = dwSts;
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
dwReturn = ERROR_SUCCESS;
|
|
|
|
ErrorExit:
|
|
fRet = (ERROR_SUCCESS == dwReturn);
|
|
if (pbSigT)
|
|
_nt_free(pbSigT, pBsafePubKey->keylen);
|
|
if (pOutput)
|
|
_nt_free(pOutput, pBsafePubKey->keylen);
|
|
if (!fRet)
|
|
SetLastError(dwReturn);
|
|
return fRet;
|
|
}
|
|
|