This source file includes following definitions.
- hack_NewCertificateListFromCertList
- ssl_ShutdownCngFunctions
- ssl_InitCngFunctions
- ssl_InitCng
- ssl_FreePlatformKey
- ssl3_CngPlatformSignHashes
- ssl3_CAPIPlatformSignHashes
- ssl3_PlatformSignHashes
- ssl_FreePlatformKey
- ssl3_GetDigestInfoPrefix
- ssl3_DSAMaxDerEncodedLength
- ssl3_PlatformSignHashes
- ssl_FreePlatformKey
- ssl3_PlatformSignHashes
#include "certt.h"
#include "cryptohi.h"
#include "keythi.h"
#include "nss.h"
#include "secitem.h"
#include "ssl.h"
#include "sslimpl.h"
#include "prerror.h"
#include "prinit.h"
#ifdef NSS_PLATFORM_CLIENT_AUTH
#ifdef XP_WIN32
#include <NCrypt.h>
#endif
#endif
#ifdef NSS_PLATFORM_CLIENT_AUTH
CERTCertificateList*
hack_NewCertificateListFromCertList(CERTCertList* list)
{
CERTCertificateList * chain = NULL;
PLArenaPool * arena = NULL;
CERTCertListNode * node;
int len;
if (CERT_LIST_EMPTY(list))
goto loser;
arena = PORT_NewArena(4096);
if (arena == NULL)
goto loser;
for (len = 0, node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
len++, node = CERT_LIST_NEXT(node)) {
}
chain = PORT_ArenaNew(arena, CERTCertificateList);
if (chain == NULL)
goto loser;
chain->certs = PORT_ArenaNewArray(arena, SECItem, len);
if (!chain->certs)
goto loser;
chain->len = len;
for (len = 0, node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
len++, node = CERT_LIST_NEXT(node)) {
if (len > 0 && (len == chain->len - 1) && node->cert->isRoot) {
chain->len = len;
break;
}
SECITEM_CopyItem(arena, &chain->certs[len], &node->cert->derCert);
}
chain->arena = arena;
return chain;
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return NULL;
}
#if defined(XP_WIN32)
typedef SECURITY_STATUS (WINAPI *NCryptFreeObjectFunc)(NCRYPT_HANDLE);
typedef SECURITY_STATUS (WINAPI *NCryptSignHashFunc)(
NCRYPT_KEY_HANDLE ,
VOID* ,
PBYTE ,
DWORD ,
PBYTE ,
DWORD ,
DWORD* ,
DWORD );
static PRCallOnceType cngFunctionsInitOnce;
static const PRCallOnceType pristineCallOnce;
static PRLibrary *ncrypt_library = NULL;
static NCryptFreeObjectFunc pNCryptFreeObject = NULL;
static NCryptSignHashFunc pNCryptSignHash = NULL;
static SECStatus
ssl_ShutdownCngFunctions(void *appData, void *nssData)
{
pNCryptSignHash = NULL;
pNCryptFreeObject = NULL;
if (ncrypt_library) {
PR_UnloadLibrary(ncrypt_library);
ncrypt_library = NULL;
}
cngFunctionsInitOnce = pristineCallOnce;
return SECSuccess;
}
static PRStatus
ssl_InitCngFunctions(void)
{
SECStatus rv;
ncrypt_library = PR_LoadLibrary("ncrypt.dll");
if (ncrypt_library == NULL)
goto loser;
pNCryptFreeObject = (NCryptFreeObjectFunc)PR_FindFunctionSymbol(
ncrypt_library, "NCryptFreeObject");
if (pNCryptFreeObject == NULL)
goto loser;
pNCryptSignHash = (NCryptSignHashFunc)PR_FindFunctionSymbol(
ncrypt_library, "NCryptSignHash");
if (pNCryptSignHash == NULL)
goto loser;
rv = NSS_RegisterShutdown(ssl_ShutdownCngFunctions, NULL);
if (rv != SECSuccess)
goto loser;
return PR_SUCCESS;
loser:
pNCryptSignHash = NULL;
pNCryptFreeObject = NULL;
if (ncrypt_library) {
PR_UnloadLibrary(ncrypt_library);
ncrypt_library = NULL;
}
return PR_FAILURE;
}
static SECStatus
ssl_InitCng(void)
{
if (PR_CallOnce(&cngFunctionsInitOnce, ssl_InitCngFunctions) != PR_SUCCESS)
return SECFailure;
return SECSuccess;
}
void
ssl_FreePlatformKey(PlatformKey key)
{
if (!key)
return;
if (key->dwKeySpec == CERT_NCRYPT_KEY_SPEC) {
if (ssl_InitCng() == SECSuccess) {
(*pNCryptFreeObject)(key->hNCryptKey);
}
} else {
CryptReleaseContext(key->hCryptProv, 0);
}
PORT_Free(key);
}
static SECStatus
ssl3_CngPlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
PRBool isTLS, KeyType keyType)
{
SECStatus rv = SECFailure;
SECURITY_STATUS ncrypt_status;
PRBool doDerEncode = PR_FALSE;
SECItem hashItem;
DWORD signatureLen = 0;
DWORD dwFlags = 0;
VOID *pPaddingInfo = NULL;
BCRYPT_PKCS1_PADDING_INFO rsaPaddingInfo;
if (key->dwKeySpec != CERT_NCRYPT_KEY_SPEC) {
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
return SECFailure;
}
if (ssl_InitCng() != SECSuccess) {
PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
return SECFailure;
}
switch (keyType) {
case rsaKey:
switch (hash->hashAlg) {
case SEC_OID_UNKNOWN:
rsaPaddingInfo.pszAlgId = NULL;
break;
case SEC_OID_SHA1:
rsaPaddingInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM;
break;
case SEC_OID_SHA256:
rsaPaddingInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM;
break;
case SEC_OID_SHA384:
rsaPaddingInfo.pszAlgId = BCRYPT_SHA384_ALGORITHM;
break;
case SEC_OID_SHA512:
rsaPaddingInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM;
break;
default:
PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
return SECFailure;
}
hashItem.data = hash->u.raw;
hashItem.len = hash->len;
dwFlags = BCRYPT_PAD_PKCS1;
pPaddingInfo = &rsaPaddingInfo;
break;
case dsaKey:
case ecKey:
if (keyType == ecKey) {
doDerEncode = PR_TRUE;
} else {
doDerEncode = isTLS;
}
if (hash->hashAlg == SEC_OID_UNKNOWN) {
hashItem.data = hash->u.s.sha;
hashItem.len = sizeof(hash->u.s.sha);
} else {
hashItem.data = hash->u.raw;
hashItem.len = hash->len;
}
break;
default:
PORT_SetError(SEC_ERROR_INVALID_KEY);
goto done;
}
PRINT_BUF(60, (NULL, "hash(es) to be signed", hashItem.data, hashItem.len));
ncrypt_status = (*pNCryptSignHash)(key->hNCryptKey, pPaddingInfo,
(PBYTE)hashItem.data, hashItem.len,
NULL, 0, &signatureLen, dwFlags);
if (FAILED(ncrypt_status) || signatureLen == 0) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, ncrypt_status);
goto done;
}
buf->data = (unsigned char *)PORT_Alloc(signatureLen);
if (!buf->data) {
goto done;
}
ncrypt_status = (*pNCryptSignHash)(key->hNCryptKey, pPaddingInfo,
(PBYTE)hashItem.data, hashItem.len,
(PBYTE)buf->data, signatureLen,
&signatureLen, dwFlags);
if (FAILED(ncrypt_status) || signatureLen == 0) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, ncrypt_status);
goto done;
}
buf->len = signatureLen;
if (doDerEncode) {
SECItem derSig = {siBuffer, NULL, 0};
rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len);
if (rv == SECSuccess) {
PORT_Free(buf->data);
*buf = derSig;
} else if (derSig.data) {
PORT_Free(derSig.data);
}
} else {
rv = SECSuccess;
}
PRINT_BUF(60, (NULL, "signed hashes", buf->data, buf->len));
done:
if (rv != SECSuccess && buf->data) {
PORT_Free(buf->data);
buf->data = NULL;
buf->len = 0;
}
return rv;
}
static SECStatus
ssl3_CAPIPlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
PRBool isTLS, KeyType keyType)
{
SECStatus rv = SECFailure;
PRBool doDerEncode = PR_FALSE;
SECItem hashItem;
DWORD argLen = 0;
DWORD signatureLen = 0;
ALG_ID hashAlg = 0;
HCRYPTHASH hHash = 0;
DWORD hashLen = 0;
unsigned int i = 0;
buf->data = NULL;
switch (hash->hashAlg) {
case SEC_OID_UNKNOWN:
hashAlg = 0;
break;
case SEC_OID_SHA1:
hashAlg = CALG_SHA1;
break;
case SEC_OID_SHA256:
hashAlg = CALG_SHA_256;
break;
case SEC_OID_SHA384:
hashAlg = CALG_SHA_384;
break;
case SEC_OID_SHA512:
hashAlg = CALG_SHA_512;
break;
default:
PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
return SECFailure;
}
switch (keyType) {
case rsaKey:
if (hashAlg == 0) {
hashAlg = CALG_SSL3_SHAMD5;
}
hashItem.data = hash->u.raw;
hashItem.len = hash->len;
break;
case dsaKey:
case ecKey:
if (keyType == ecKey) {
doDerEncode = PR_TRUE;
} else {
doDerEncode = isTLS;
}
if (hashAlg == 0) {
hashAlg = CALG_SHA1;
hashItem.data = hash->u.s.sha;
hashItem.len = sizeof(hash->u.s.sha);
} else {
hashItem.data = hash->u.raw;
hashItem.len = hash->len;
}
break;
default:
PORT_SetError(SEC_ERROR_INVALID_KEY);
goto done;
}
PRINT_BUF(60, (NULL, "hash(es) to be signed", hashItem.data, hashItem.len));
if (!CryptCreateHash(key->hCryptProv, hashAlg, 0, 0, &hHash)) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, GetLastError());
goto done;
}
argLen = sizeof(hashLen);
if (!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&hashLen, &argLen, 0)) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, GetLastError());
goto done;
}
if (hashLen != hashItem.len) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, 0);
goto done;
}
if (!CryptSetHashParam(hHash, HP_HASHVAL, (BYTE*)hashItem.data, 0)) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, GetLastError());
goto done;
}
if (!CryptSignHash(hHash, key->dwKeySpec, NULL, 0,
NULL, &signatureLen) || signatureLen == 0) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, GetLastError());
goto done;
}
buf->data = (unsigned char *)PORT_Alloc(signatureLen);
if (!buf->data)
goto done;
if (!CryptSignHash(hHash, key->dwKeySpec, NULL, 0,
(BYTE*)buf->data, &signatureLen)) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, GetLastError());
goto done;
}
buf->len = signatureLen;
for (i = 0; i < buf->len / 2; ++i) {
unsigned char tmp = buf->data[i];
buf->data[i] = buf->data[buf->len - 1 - i];
buf->data[buf->len - 1 - i] = tmp;
}
if (doDerEncode) {
SECItem derSig = {siBuffer, NULL, 0};
rv = DSAU_EncodeDerSigWithLen(&derSig, buf, buf->len);
if (rv == SECSuccess) {
PORT_Free(buf->data);
*buf = derSig;
} else if (derSig.data) {
PORT_Free(derSig.data);
}
} else {
rv = SECSuccess;
}
PRINT_BUF(60, (NULL, "signed hashes", buf->data, buf->len));
done:
if (hHash)
CryptDestroyHash(hHash);
if (rv != SECSuccess && buf->data) {
PORT_Free(buf->data);
buf->data = NULL;
}
return rv;
}
SECStatus
ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
PRBool isTLS, KeyType keyType)
{
if (key->dwKeySpec == CERT_NCRYPT_KEY_SPEC) {
return ssl3_CngPlatformSignHashes(hash, key, buf, isTLS, keyType);
}
return ssl3_CAPIPlatformSignHashes(hash, key, buf, isTLS, keyType);
}
#elif defined(XP_MACOSX)
#include <Security/cssm.h>
void
ssl_FreePlatformKey(PlatformKey key)
{
CFRelease(key);
}
#define SSL_MAX_DIGEST_INFO_PREFIX 20
static SECStatus
ssl3_GetDigestInfoPrefix(SECOidTag hashAlg,
const SSL3Opaque** out, unsigned int *out_len)
{
static const unsigned char kSHA1[] = {
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14
};
static const unsigned char kSHA224[] = {
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
0x00, 0x04, 0x1c
};
static const unsigned char kSHA256[] = {
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
0x00, 0x04, 0x20
};
static const unsigned char kSHA384[] = {
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
0x00, 0x04, 0x30
};
static const unsigned char kSHA512[] = {
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40
};
switch (hashAlg) {
case SEC_OID_UNKNOWN:
*out_len = 0;
break;
case SEC_OID_SHA1:
*out = kSHA1;
*out_len = sizeof(kSHA1);
break;
case SEC_OID_SHA224:
*out = kSHA224;
*out_len = sizeof(kSHA224);
break;
case SEC_OID_SHA256:
*out = kSHA256;
*out_len = sizeof(kSHA256);
break;
case SEC_OID_SHA384:
*out = kSHA384;
*out_len = sizeof(kSHA384);
break;
case SEC_OID_SHA512:
*out = kSHA512;
*out_len = sizeof(kSHA512);
break;
default:
PORT_SetError(SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM);
return SECFailure;
}
return SECSuccess;
}
static unsigned int
ssl3_DSAMaxDerEncodedLength(unsigned int rawDsaLen)
{
unsigned int integerDerLen = rawDsaLen/2 +
1 +
SEC_ASN1LengthLength(rawDsaLen/2 + 1) +
1;
return 2 * integerDerLen +
SEC_ASN1LengthLength(2 * integerDerLen) +
1;
}
SECStatus
ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
PRBool isTLS, KeyType keyType)
{
SECStatus rv = SECFailure;
PRBool doDerDecode = PR_FALSE;
unsigned int rawDsaLen;
unsigned int signatureLen;
OSStatus status = noErr;
CSSM_CSP_HANDLE cspHandle = 0;
const CSSM_KEY *cssmKey = NULL;
CSSM_ALGORITHMS sigAlg;
CSSM_ALGORITHMS digestAlg;
const CSSM_ACCESS_CREDENTIALS * cssmCreds = NULL;
CSSM_RETURN cssmRv;
CSSM_DATA hashData;
CSSM_DATA signatureData;
CSSM_CC_HANDLE cssmSignature = 0;
const SSL3Opaque* prefix;
unsigned int prefixLen;
SSL3Opaque prefixAndHash[SSL_MAX_DIGEST_INFO_PREFIX + HASH_LENGTH_MAX];
buf->data = NULL;
status = SecKeyGetCSPHandle(key, &cspHandle);
if (status != noErr) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
goto done;
}
status = SecKeyGetCSSMKey(key, &cssmKey);
if (status != noErr || !cssmKey) {
PORT_SetError(SEC_ERROR_NO_KEY);
goto done;
}
sigAlg = cssmKey->KeyHeader.AlgorithmId;
digestAlg = CSSM_ALGID_NONE;
switch (keyType) {
case rsaKey:
PORT_Assert(sigAlg == CSSM_ALGID_RSA);
signatureLen = (cssmKey->KeyHeader.LogicalKeySizeInBits + 7) / 8;
if (ssl3_GetDigestInfoPrefix(hash->hashAlg, &prefix, &prefixLen) !=
SECSuccess) {
goto done;
}
if (prefixLen + hash->len > sizeof(prefixAndHash)) {
PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
goto done;
}
memcpy(prefixAndHash, prefix, prefixLen);
memcpy(prefixAndHash + prefixLen, hash->u.raw, hash->len);
hashData.Data = prefixAndHash;
hashData.Length = prefixLen + hash->len;
break;
case dsaKey:
case ecKey:
doDerDecode = (keyType == dsaKey) && !isTLS;
if (keyType == ecKey) {
PORT_Assert(sigAlg == CSSM_ALGID_ECDSA);
rawDsaLen =
(cssmKey->KeyHeader.LogicalKeySizeInBits + 7) / 8 * 2;
} else {
PORT_Assert(sigAlg == CSSM_ALGID_DSA);
rawDsaLen = 2 * (160 / 8);
}
signatureLen = ssl3_DSAMaxDerEncodedLength(rawDsaLen);
if (hash->hashAlg == SEC_OID_UNKNOWN) {
hashData.Data = hash->u.s.sha;
hashData.Length = sizeof(hash->u.s.sha);
} else {
hashData.Data = hash->u.raw;
hashData.Length = hash->len;
}
break;
default:
PORT_SetError(SEC_ERROR_INVALID_KEY);
goto done;
}
PRINT_BUF(60, (NULL, "hash(es) to be signed", hashData.Data, hashData.Length));
if (signatureLen == 0) {
PORT_SetError(SEC_ERROR_INVALID_KEY);
goto done;
}
buf->data = (unsigned char *)PORT_Alloc(signatureLen);
if (!buf->data)
goto done;
status = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN,
kSecCredentialTypeDefault, &cssmCreds);
if (status != noErr) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, status);
goto done;
}
signatureData.Length = signatureLen;
signatureData.Data = (uint8*)buf->data;
cssmRv = CSSM_CSP_CreateSignatureContext(cspHandle, sigAlg, cssmCreds,
cssmKey, &cssmSignature);
if (cssmRv) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, cssmRv);
goto done;
}
if (cssmKey->KeyHeader.AlgorithmId == CSSM_ALGID_RSA) {
CSSM_CONTEXT_ATTRIBUTE blindingAttr;
blindingAttr.AttributeType = CSSM_ATTRIBUTE_RSA_BLINDING;
blindingAttr.AttributeLength = sizeof(uint32);
blindingAttr.Attribute.Uint32 = 1;
cssmRv = CSSM_UpdateContextAttributes(cssmSignature, 1, &blindingAttr);
if (cssmRv) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, cssmRv);
goto done;
}
}
cssmRv = CSSM_SignData(cssmSignature, &hashData, 1, digestAlg,
&signatureData);
if (cssmRv) {
PR_SetError(SSL_ERROR_SIGN_HASHES_FAILURE, cssmRv);
goto done;
}
buf->len = signatureData.Length;
if (doDerDecode) {
SECItem* rawSig = DSAU_DecodeDerSigToLen(buf, rawDsaLen);
if (rawSig != NULL) {
PORT_Free(buf->data);
*buf = *rawSig;
PORT_Free(rawSig);
rv = SECSuccess;
}
} else {
rv = SECSuccess;
}
PRINT_BUF(60, (NULL, "signed hashes", buf->data, buf->len));
done:
if (cssmSignature)
CSSM_DeleteContext(cssmSignature);
if (rv != SECSuccess && buf->data) {
PORT_Free(buf->data);
buf->data = NULL;
}
return rv;
}
#else
void
ssl_FreePlatformKey(PlatformKey key)
{
}
SECStatus
ssl3_PlatformSignHashes(SSL3Hashes *hash, PlatformKey key, SECItem *buf,
PRBool isTLS, KeyType keyType)
{
PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
return SECFailure;
}
#endif
#endif