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