michael@0: /* michael@0: * Key Derivation that doesn't use PKCS11 michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ssl.h" /* prereq to sslimpl.h */ michael@0: #include "certt.h" /* prereq to sslimpl.h */ michael@0: #include "keythi.h" /* prereq to sslimpl.h */ michael@0: #include "sslimpl.h" michael@0: #ifndef NO_PKCS11_BYPASS michael@0: #include "blapi.h" michael@0: #endif michael@0: michael@0: #include "keyhi.h" michael@0: #include "pk11func.h" michael@0: #include "secasn1.h" michael@0: #include "cert.h" michael@0: #include "secmodt.h" michael@0: michael@0: #include "sslproto.h" michael@0: #include "sslerr.h" michael@0: michael@0: #ifndef NO_PKCS11_BYPASS michael@0: /* make this a macro! */ michael@0: #ifdef NOT_A_MACRO michael@0: static void michael@0: buildSSLKey(unsigned char * keyBlock, unsigned int keyLen, SECItem * result, michael@0: const char * label) michael@0: { michael@0: result->type = siBuffer; michael@0: result->data = keyBlock; michael@0: result->len = keyLen; michael@0: PRINT_BUF(100, (NULL, label, keyBlock, keyLen)); michael@0: } michael@0: #else michael@0: #define buildSSLKey(keyBlock, keyLen, result, label) \ michael@0: { \ michael@0: (result)->type = siBuffer; \ michael@0: (result)->data = keyBlock; \ michael@0: (result)->len = keyLen; \ michael@0: PRINT_BUF(100, (NULL, label, keyBlock, keyLen)); \ michael@0: } michael@0: #endif michael@0: michael@0: /* michael@0: * SSL Key generation given pre master secret michael@0: */ michael@0: #ifndef NUM_MIXERS michael@0: #define NUM_MIXERS 9 michael@0: #endif michael@0: static const char * const mixers[NUM_MIXERS] = { michael@0: "A", michael@0: "BB", michael@0: "CCC", michael@0: "DDDD", michael@0: "EEEEE", michael@0: "FFFFFF", michael@0: "GGGGGGG", michael@0: "HHHHHHHH", michael@0: "IIIIIIIII" michael@0: }; michael@0: michael@0: michael@0: SECStatus michael@0: ssl3_KeyAndMacDeriveBypass( michael@0: ssl3CipherSpec * pwSpec, michael@0: const unsigned char * cr, michael@0: const unsigned char * sr, michael@0: PRBool isTLS, michael@0: PRBool isExport) michael@0: { michael@0: const ssl3BulkCipherDef *cipher_def = pwSpec->cipher_def; michael@0: unsigned char * key_block = pwSpec->key_block; michael@0: unsigned char * key_block2 = NULL; michael@0: unsigned int block_bytes = 0; michael@0: unsigned int block_needed = 0; michael@0: unsigned int i; michael@0: unsigned int keySize; /* actual size of cipher keys */ michael@0: unsigned int effKeySize; /* effective size of cipher keys */ michael@0: unsigned int macSize; /* size of MAC secret */ michael@0: unsigned int IVSize; /* size of IV */ michael@0: PRBool explicitIV = PR_FALSE; michael@0: SECStatus rv = SECFailure; michael@0: SECStatus status = SECSuccess; michael@0: PRBool isFIPS = PR_FALSE; michael@0: PRBool isTLS12 = pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2; michael@0: michael@0: SECItem srcr; michael@0: SECItem crsr; michael@0: michael@0: unsigned char srcrdata[SSL3_RANDOM_LENGTH * 2]; michael@0: unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2]; michael@0: PRUint64 md5buf[22]; michael@0: PRUint64 shabuf[40]; michael@0: michael@0: #define md5Ctx ((MD5Context *)md5buf) michael@0: #define shaCtx ((SHA1Context *)shabuf) michael@0: michael@0: static const SECItem zed = { siBuffer, NULL, 0 }; michael@0: michael@0: if (pwSpec->msItem.data == NULL || michael@0: pwSpec->msItem.len != SSL3_MASTER_SECRET_LENGTH) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return rv; michael@0: } michael@0: michael@0: PRINT_BUF(100, (NULL, "Master Secret", pwSpec->msItem.data, michael@0: pwSpec->msItem.len)); michael@0: michael@0: /* figure out how much is needed */ michael@0: macSize = pwSpec->mac_size; michael@0: keySize = cipher_def->key_size; michael@0: effKeySize = cipher_def->secret_key_size; michael@0: IVSize = cipher_def->iv_size; michael@0: if (keySize == 0) { michael@0: effKeySize = IVSize = 0; /* only MACing */ michael@0: } michael@0: if (cipher_def->type == type_block && michael@0: pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_1) { michael@0: /* Block ciphers in >= TLS 1.1 use a per-record, explicit IV. */ michael@0: explicitIV = PR_TRUE; michael@0: } michael@0: block_needed = michael@0: 2 * (macSize + effKeySize + ((!isExport && !explicitIV) * IVSize)); michael@0: michael@0: /* michael@0: * clear out our returned keys so we can recover on failure michael@0: */ michael@0: pwSpec->client.write_key_item = zed; michael@0: pwSpec->client.write_mac_key_item = zed; michael@0: pwSpec->server.write_key_item = zed; michael@0: pwSpec->server.write_mac_key_item = zed; michael@0: michael@0: /* initialize the server random, client random block */ michael@0: srcr.type = siBuffer; michael@0: srcr.data = srcrdata; michael@0: srcr.len = sizeof srcrdata; michael@0: PORT_Memcpy(srcrdata, sr, SSL3_RANDOM_LENGTH); michael@0: PORT_Memcpy(srcrdata + SSL3_RANDOM_LENGTH, cr, SSL3_RANDOM_LENGTH); michael@0: michael@0: /* initialize the client random, server random block */ michael@0: crsr.type = siBuffer; michael@0: crsr.data = crsrdata; michael@0: crsr.len = sizeof crsrdata; michael@0: PORT_Memcpy(crsrdata, cr, SSL3_RANDOM_LENGTH); michael@0: PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, sr, SSL3_RANDOM_LENGTH); michael@0: PRINT_BUF(100, (NULL, "Key & MAC CRSR", crsr.data, crsr.len)); michael@0: michael@0: /* michael@0: * generate the key material: michael@0: */ michael@0: if (isTLS) { michael@0: SECItem keyblk; michael@0: michael@0: keyblk.type = siBuffer; michael@0: keyblk.data = key_block; michael@0: keyblk.len = block_needed; michael@0: michael@0: if (isTLS12) { michael@0: status = TLS_P_hash(HASH_AlgSHA256, &pwSpec->msItem, michael@0: "key expansion", &srcr, &keyblk, isFIPS); michael@0: } else { michael@0: status = TLS_PRF(&pwSpec->msItem, "key expansion", &srcr, &keyblk, michael@0: isFIPS); michael@0: } michael@0: if (status != SECSuccess) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: block_bytes = keyblk.len; michael@0: } else { michael@0: /* key_block = michael@0: * MD5(master_secret + SHA('A' + master_secret + michael@0: * ServerHello.random + ClientHello.random)) + michael@0: * MD5(master_secret + SHA('BB' + master_secret + michael@0: * ServerHello.random + ClientHello.random)) + michael@0: * MD5(master_secret + SHA('CCC' + master_secret + michael@0: * ServerHello.random + ClientHello.random)) + michael@0: * [...]; michael@0: */ michael@0: unsigned int made = 0; michael@0: for (i = 0; made < block_needed && i < NUM_MIXERS; ++i) { michael@0: unsigned int outLen; michael@0: unsigned char sha_out[SHA1_LENGTH]; michael@0: michael@0: SHA1_Begin(shaCtx); michael@0: SHA1_Update(shaCtx, (unsigned char*)(mixers[i]), i+1); michael@0: SHA1_Update(shaCtx, pwSpec->msItem.data, pwSpec->msItem.len); michael@0: SHA1_Update(shaCtx, srcr.data, srcr.len); michael@0: SHA1_End(shaCtx, sha_out, &outLen, SHA1_LENGTH); michael@0: PORT_Assert(outLen == SHA1_LENGTH); michael@0: michael@0: MD5_Begin(md5Ctx); michael@0: MD5_Update(md5Ctx, pwSpec->msItem.data, pwSpec->msItem.len); michael@0: MD5_Update(md5Ctx, sha_out, outLen); michael@0: MD5_End(md5Ctx, key_block + made, &outLen, MD5_LENGTH); michael@0: PORT_Assert(outLen == MD5_LENGTH); michael@0: made += MD5_LENGTH; michael@0: } michael@0: block_bytes = made; michael@0: } michael@0: PORT_Assert(block_bytes >= block_needed); michael@0: PORT_Assert(block_bytes <= sizeof pwSpec->key_block); michael@0: PRINT_BUF(100, (NULL, "key block", key_block, block_bytes)); michael@0: michael@0: /* michael@0: * Put the key material where it goes. michael@0: */ michael@0: key_block2 = key_block + block_bytes; michael@0: i = 0; /* now shows how much consumed */ michael@0: michael@0: /* michael@0: * The key_block is partitioned as follows: michael@0: * client_write_MAC_secret[CipherSpec.hash_size] michael@0: */ michael@0: buildSSLKey(&key_block[i],macSize, &pwSpec->client.write_mac_key_item, \ michael@0: "Client Write MAC Secret"); michael@0: i += macSize; michael@0: michael@0: /* michael@0: * server_write_MAC_secret[CipherSpec.hash_size] michael@0: */ michael@0: buildSSLKey(&key_block[i],macSize, &pwSpec->server.write_mac_key_item, \ michael@0: "Server Write MAC Secret"); michael@0: i += macSize; michael@0: michael@0: if (!keySize) { michael@0: /* only MACing */ michael@0: buildSSLKey(NULL, 0, &pwSpec->client.write_key_item, \ michael@0: "Client Write Key (MAC only)"); michael@0: buildSSLKey(NULL, 0, &pwSpec->server.write_key_item, \ michael@0: "Server Write Key (MAC only)"); michael@0: buildSSLKey(NULL, 0, &pwSpec->client.write_iv_item, \ michael@0: "Client Write IV (MAC only)"); michael@0: buildSSLKey(NULL, 0, &pwSpec->server.write_iv_item, \ michael@0: "Server Write IV (MAC only)"); michael@0: } else if (!isExport) { michael@0: /* michael@0: ** Generate Domestic write keys and IVs. michael@0: ** client_write_key[CipherSpec.key_material] michael@0: */ michael@0: buildSSLKey(&key_block[i], keySize, &pwSpec->client.write_key_item, \ michael@0: "Domestic Client Write Key"); michael@0: i += keySize; michael@0: michael@0: /* michael@0: ** server_write_key[CipherSpec.key_material] michael@0: */ michael@0: buildSSLKey(&key_block[i], keySize, &pwSpec->server.write_key_item, \ michael@0: "Domestic Server Write Key"); michael@0: i += keySize; michael@0: michael@0: if (IVSize > 0) { michael@0: if (explicitIV) { michael@0: static unsigned char zero_block[32]; michael@0: PORT_Assert(IVSize <= sizeof zero_block); michael@0: buildSSLKey(&zero_block[0], IVSize, \ michael@0: &pwSpec->client.write_iv_item, \ michael@0: "Domestic Client Write IV"); michael@0: buildSSLKey(&zero_block[0], IVSize, \ michael@0: &pwSpec->server.write_iv_item, \ michael@0: "Domestic Server Write IV"); michael@0: } else { michael@0: /* michael@0: ** client_write_IV[CipherSpec.IV_size] michael@0: */ michael@0: buildSSLKey(&key_block[i], IVSize, \ michael@0: &pwSpec->client.write_iv_item, \ michael@0: "Domestic Client Write IV"); michael@0: i += IVSize; michael@0: michael@0: /* michael@0: ** server_write_IV[CipherSpec.IV_size] michael@0: */ michael@0: buildSSLKey(&key_block[i], IVSize, \ michael@0: &pwSpec->server.write_iv_item, \ michael@0: "Domestic Server Write IV"); michael@0: i += IVSize; michael@0: } michael@0: } michael@0: PORT_Assert(i <= block_bytes); michael@0: } else if (!isTLS) { michael@0: /* michael@0: ** Generate SSL3 Export write keys and IVs. michael@0: */ michael@0: unsigned int outLen; michael@0: michael@0: /* michael@0: ** client_write_key[CipherSpec.key_material] michael@0: ** final_client_write_key = MD5(client_write_key + michael@0: ** ClientHello.random + ServerHello.random); michael@0: */ michael@0: MD5_Begin(md5Ctx); michael@0: MD5_Update(md5Ctx, &key_block[i], effKeySize); michael@0: MD5_Update(md5Ctx, crsr.data, crsr.len); michael@0: MD5_End(md5Ctx, key_block2, &outLen, MD5_LENGTH); michael@0: i += effKeySize; michael@0: buildSSLKey(key_block2, keySize, &pwSpec->client.write_key_item, \ michael@0: "SSL3 Export Client Write Key"); michael@0: key_block2 += keySize; michael@0: michael@0: /* michael@0: ** server_write_key[CipherSpec.key_material] michael@0: ** final_server_write_key = MD5(server_write_key + michael@0: ** ServerHello.random + ClientHello.random); michael@0: */ michael@0: MD5_Begin(md5Ctx); michael@0: MD5_Update(md5Ctx, &key_block[i], effKeySize); michael@0: MD5_Update(md5Ctx, srcr.data, srcr.len); michael@0: MD5_End(md5Ctx, key_block2, &outLen, MD5_LENGTH); michael@0: i += effKeySize; michael@0: buildSSLKey(key_block2, keySize, &pwSpec->server.write_key_item, \ michael@0: "SSL3 Export Server Write Key"); michael@0: key_block2 += keySize; michael@0: PORT_Assert(i <= block_bytes); michael@0: michael@0: if (IVSize) { michael@0: /* michael@0: ** client_write_IV = michael@0: ** MD5(ClientHello.random + ServerHello.random); michael@0: */ michael@0: MD5_Begin(md5Ctx); michael@0: MD5_Update(md5Ctx, crsr.data, crsr.len); michael@0: MD5_End(md5Ctx, key_block2, &outLen, MD5_LENGTH); michael@0: buildSSLKey(key_block2, IVSize, &pwSpec->client.write_iv_item, \ michael@0: "SSL3 Export Client Write IV"); michael@0: key_block2 += IVSize; michael@0: michael@0: /* michael@0: ** server_write_IV = michael@0: ** MD5(ServerHello.random + ClientHello.random); michael@0: */ michael@0: MD5_Begin(md5Ctx); michael@0: MD5_Update(md5Ctx, srcr.data, srcr.len); michael@0: MD5_End(md5Ctx, key_block2, &outLen, MD5_LENGTH); michael@0: buildSSLKey(key_block2, IVSize, &pwSpec->server.write_iv_item, \ michael@0: "SSL3 Export Server Write IV"); michael@0: key_block2 += IVSize; michael@0: } michael@0: michael@0: PORT_Assert(key_block2 - key_block <= sizeof pwSpec->key_block); michael@0: } else { michael@0: /* michael@0: ** Generate TLS Export write keys and IVs. michael@0: */ michael@0: SECItem secret ; michael@0: SECItem keyblk ; michael@0: michael@0: secret.type = siBuffer; michael@0: keyblk.type = siBuffer; michael@0: /* michael@0: ** client_write_key[CipherSpec.key_material] michael@0: ** final_client_write_key = PRF(client_write_key, michael@0: ** "client write key", michael@0: ** client_random + server_random); michael@0: */ michael@0: secret.data = &key_block[i]; michael@0: secret.len = effKeySize; michael@0: i += effKeySize; michael@0: keyblk.data = key_block2; michael@0: keyblk.len = keySize; michael@0: status = TLS_PRF(&secret, "client write key", &crsr, &keyblk, isFIPS); michael@0: if (status != SECSuccess) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: buildSSLKey(key_block2, keySize, &pwSpec->client.write_key_item, \ michael@0: "TLS Export Client Write Key"); michael@0: key_block2 += keySize; michael@0: michael@0: /* michael@0: ** server_write_key[CipherSpec.key_material] michael@0: ** final_server_write_key = PRF(server_write_key, michael@0: ** "server write key", michael@0: ** client_random + server_random); michael@0: */ michael@0: secret.data = &key_block[i]; michael@0: secret.len = effKeySize; michael@0: i += effKeySize; michael@0: keyblk.data = key_block2; michael@0: keyblk.len = keySize; michael@0: status = TLS_PRF(&secret, "server write key", &crsr, &keyblk, isFIPS); michael@0: if (status != SECSuccess) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: buildSSLKey(key_block2, keySize, &pwSpec->server.write_key_item, \ michael@0: "TLS Export Server Write Key"); michael@0: key_block2 += keySize; michael@0: michael@0: /* michael@0: ** iv_block = PRF("", "IV block", client_random + server_random); michael@0: ** client_write_IV[SecurityParameters.IV_size] michael@0: ** server_write_IV[SecurityParameters.IV_size] michael@0: */ michael@0: if (IVSize) { michael@0: secret.data = NULL; michael@0: secret.len = 0; michael@0: keyblk.data = key_block2; michael@0: keyblk.len = 2 * IVSize; michael@0: status = TLS_PRF(&secret, "IV block", &crsr, &keyblk, isFIPS); michael@0: if (status != SECSuccess) { michael@0: goto key_and_mac_derive_fail; michael@0: } michael@0: buildSSLKey(key_block2, IVSize, \ michael@0: &pwSpec->client.write_iv_item, \ michael@0: "TLS Export Client Write IV"); michael@0: buildSSLKey(key_block2 + IVSize, IVSize, \ michael@0: &pwSpec->server.write_iv_item, \ michael@0: "TLS Export Server Write IV"); michael@0: key_block2 += 2 * IVSize; michael@0: } michael@0: PORT_Assert(key_block2 - key_block <= sizeof pwSpec->key_block); michael@0: } michael@0: rv = SECSuccess; michael@0: michael@0: key_and_mac_derive_fail: michael@0: michael@0: MD5_DestroyContext(md5Ctx, PR_FALSE); michael@0: SHA1_DestroyContext(shaCtx, PR_FALSE); michael@0: michael@0: if (rv != SECSuccess) { michael@0: PORT_SetError(SSL_ERROR_SESSION_KEY_GEN_FAILURE); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: /* derive the Master Secret from the PMS */ michael@0: /* Presently, this is only done wtih RSA PMS, and only on the server side, michael@0: * so isRSA is always true. michael@0: */ michael@0: SECStatus michael@0: ssl3_MasterKeyDeriveBypass( michael@0: ssl3CipherSpec * pwSpec, michael@0: const unsigned char * cr, michael@0: const unsigned char * sr, michael@0: const SECItem * pms, michael@0: PRBool isTLS, michael@0: PRBool isRSA) michael@0: { michael@0: unsigned char * key_block = pwSpec->key_block; michael@0: SECStatus rv = SECSuccess; michael@0: PRBool isFIPS = PR_FALSE; michael@0: PRBool isTLS12 = pwSpec->version >= SSL_LIBRARY_VERSION_TLS_1_2; michael@0: michael@0: SECItem crsr; michael@0: michael@0: unsigned char crsrdata[SSL3_RANDOM_LENGTH * 2]; michael@0: PRUint64 md5buf[22]; michael@0: PRUint64 shabuf[40]; michael@0: michael@0: #define md5Ctx ((MD5Context *)md5buf) michael@0: #define shaCtx ((SHA1Context *)shabuf) michael@0: michael@0: /* first do the consistancy checks */ michael@0: if (isRSA) { michael@0: PORT_Assert(pms->len == SSL3_RSA_PMS_LENGTH); michael@0: if (pms->len != SSL3_RSA_PMS_LENGTH) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: /* caller must test PMS version for rollback */ michael@0: } michael@0: michael@0: /* initialize the client random, server random block */ michael@0: crsr.type = siBuffer; michael@0: crsr.data = crsrdata; michael@0: crsr.len = sizeof crsrdata; michael@0: PORT_Memcpy(crsrdata, cr, SSL3_RANDOM_LENGTH); michael@0: PORT_Memcpy(crsrdata + SSL3_RANDOM_LENGTH, sr, SSL3_RANDOM_LENGTH); michael@0: PRINT_BUF(100, (NULL, "Master Secret CRSR", crsr.data, crsr.len)); michael@0: michael@0: /* finally do the key gen */ michael@0: if (isTLS) { michael@0: SECItem master = { siBuffer, NULL, 0 }; michael@0: michael@0: master.data = key_block; michael@0: master.len = SSL3_MASTER_SECRET_LENGTH; michael@0: michael@0: if (isTLS12) { michael@0: rv = TLS_P_hash(HASH_AlgSHA256, pms, "master secret", &crsr, michael@0: &master, isFIPS); michael@0: } else { michael@0: rv = TLS_PRF(pms, "master secret", &crsr, &master, isFIPS); michael@0: } michael@0: if (rv != SECSuccess) { michael@0: PORT_SetError(SSL_ERROR_SESSION_KEY_GEN_FAILURE); michael@0: } michael@0: } else { michael@0: int i; michael@0: unsigned int made = 0; michael@0: for (i = 0; i < 3; i++) { michael@0: unsigned int outLen; michael@0: unsigned char sha_out[SHA1_LENGTH]; michael@0: michael@0: SHA1_Begin(shaCtx); michael@0: SHA1_Update(shaCtx, (unsigned char*) mixers[i], i+1); michael@0: SHA1_Update(shaCtx, pms->data, pms->len); michael@0: SHA1_Update(shaCtx, crsr.data, crsr.len); michael@0: SHA1_End(shaCtx, sha_out, &outLen, SHA1_LENGTH); michael@0: PORT_Assert(outLen == SHA1_LENGTH); michael@0: michael@0: MD5_Begin(md5Ctx); michael@0: MD5_Update(md5Ctx, pms->data, pms->len); michael@0: MD5_Update(md5Ctx, sha_out, outLen); michael@0: MD5_End(md5Ctx, key_block + made, &outLen, MD5_LENGTH); michael@0: PORT_Assert(outLen == MD5_LENGTH); michael@0: made += outLen; michael@0: } michael@0: } michael@0: michael@0: /* store the results */ michael@0: PORT_Memcpy(pwSpec->raw_master_secret, key_block, michael@0: SSL3_MASTER_SECRET_LENGTH); michael@0: pwSpec->msItem.data = pwSpec->raw_master_secret; michael@0: pwSpec->msItem.len = SSL3_MASTER_SECRET_LENGTH; michael@0: PRINT_BUF(100, (NULL, "Master Secret", pwSpec->msItem.data, michael@0: pwSpec->msItem.len)); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static SECStatus michael@0: ssl_canExtractMS(PK11SymKey *pms, PRBool isTLS, PRBool isDH, PRBool *pcbp) michael@0: { SECStatus rv; michael@0: PK11SymKey * ms = NULL; michael@0: SECItem params = {siBuffer, NULL, 0}; michael@0: CK_SSL3_MASTER_KEY_DERIVE_PARAMS master_params; michael@0: unsigned char rand[SSL3_RANDOM_LENGTH]; michael@0: CK_VERSION pms_version; michael@0: CK_MECHANISM_TYPE master_derive; michael@0: CK_MECHANISM_TYPE key_derive; michael@0: CK_FLAGS keyFlags; michael@0: michael@0: if (pms == NULL) michael@0: return(SECFailure); michael@0: michael@0: PORT_Memset(rand, 0, SSL3_RANDOM_LENGTH); michael@0: michael@0: if (isTLS) { michael@0: if(isDH) master_derive = CKM_TLS_MASTER_KEY_DERIVE_DH; michael@0: else master_derive = CKM_TLS_MASTER_KEY_DERIVE; michael@0: key_derive = CKM_TLS_KEY_AND_MAC_DERIVE; michael@0: keyFlags = CKF_SIGN | CKF_VERIFY; michael@0: } else { michael@0: if (isDH) master_derive = CKM_SSL3_MASTER_KEY_DERIVE_DH; michael@0: else master_derive = CKM_SSL3_MASTER_KEY_DERIVE; michael@0: key_derive = CKM_SSL3_KEY_AND_MAC_DERIVE; michael@0: keyFlags = 0; michael@0: } michael@0: michael@0: master_params.pVersion = &pms_version; michael@0: master_params.RandomInfo.pClientRandom = rand; michael@0: master_params.RandomInfo.ulClientRandomLen = SSL3_RANDOM_LENGTH; michael@0: master_params.RandomInfo.pServerRandom = rand; michael@0: master_params.RandomInfo.ulServerRandomLen = SSL3_RANDOM_LENGTH; michael@0: michael@0: params.data = (unsigned char *) &master_params; michael@0: params.len = sizeof master_params; michael@0: michael@0: ms = PK11_DeriveWithFlags(pms, master_derive, ¶ms, key_derive, michael@0: CKA_DERIVE, 0, keyFlags); michael@0: if (ms == NULL) michael@0: return(SECFailure); michael@0: michael@0: rv = PK11_ExtractKeyValue(ms); michael@0: *pcbp = (rv == SECSuccess); michael@0: PK11_FreeSymKey(ms); michael@0: michael@0: return(rv); michael@0: michael@0: } michael@0: #endif /* !NO_PKCS11_BYPASS */ michael@0: michael@0: /* Check the key exchange algorithm for each cipher in the list to see if michael@0: * a master secret key can be extracted. If the KEA will use keys from the michael@0: * specified cert make sure the extract operation is attempted from the slot michael@0: * where the private key resides. michael@0: * If MS can be extracted for all ciphers, (*pcanbypass) is set to TRUE and michael@0: * SECSuccess is returned. In all other cases but one (*pcanbypass) is michael@0: * set to FALSE and SECFailure is returned. michael@0: * In that last case Derive() has been called successfully but the MS is null, michael@0: * CanBypass sets (*pcanbypass) to FALSE and returns SECSuccess indicating the michael@0: * arguments were all valid but the slot cannot be bypassed. michael@0: */ michael@0: michael@0: /* XXX Add SSL_CBP_TLS1_1 and test it in protocolmask when setting isTLS. */ michael@0: michael@0: SECStatus michael@0: SSL_CanBypass(CERTCertificate *cert, SECKEYPrivateKey *srvPrivkey, michael@0: PRUint32 protocolmask, PRUint16 *ciphersuites, int nsuites, michael@0: PRBool *pcanbypass, void *pwArg) michael@0: { michael@0: #ifdef NO_PKCS11_BYPASS michael@0: if (!pcanbypass) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: *pcanbypass = PR_FALSE; michael@0: return SECSuccess; michael@0: #else michael@0: SECStatus rv; michael@0: int i; michael@0: PRUint16 suite; michael@0: PK11SymKey * pms = NULL; michael@0: SECKEYPublicKey * srvPubkey = NULL; michael@0: KeyType privKeytype; michael@0: PK11SlotInfo * slot = NULL; michael@0: SECItem param; michael@0: CK_VERSION version; michael@0: CK_MECHANISM_TYPE mechanism_array[2]; michael@0: SECItem enc_pms = {siBuffer, NULL, 0}; michael@0: PRBool isTLS = PR_FALSE; michael@0: SSLCipherSuiteInfo csdef; michael@0: PRBool testrsa = PR_FALSE; michael@0: PRBool testrsa_export = PR_FALSE; michael@0: PRBool testecdh = PR_FALSE; michael@0: PRBool testecdhe = PR_FALSE; michael@0: #ifndef NSS_DISABLE_ECC michael@0: SECKEYECParams ecParams = { siBuffer, NULL, 0 }; michael@0: #endif michael@0: michael@0: if (!cert || !srvPrivkey || !ciphersuites || !pcanbypass) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: srvPubkey = CERT_ExtractPublicKey(cert); michael@0: if (!srvPubkey) michael@0: return SECFailure; michael@0: michael@0: *pcanbypass = PR_TRUE; michael@0: rv = SECFailure; michael@0: michael@0: /* determine which KEAs to test */ michael@0: /* 0 (TLS_NULL_WITH_NULL_NULL) is used as a list terminator because michael@0: * SSL3 and TLS specs forbid negotiating that cipher suite number. michael@0: */ michael@0: for (i=0; i < nsuites && (suite = *ciphersuites++) != 0; i++) { michael@0: /* skip SSL2 cipher suites and ones NSS doesn't support */ michael@0: if (SSL_GetCipherSuiteInfo(suite, &csdef, sizeof(csdef)) != SECSuccess michael@0: || SSL_IS_SSL2_CIPHER(suite) ) michael@0: continue; michael@0: switch (csdef.keaType) { michael@0: case ssl_kea_rsa: michael@0: switch (csdef.cipherSuite) { michael@0: case TLS_RSA_EXPORT1024_WITH_RC4_56_SHA: michael@0: case TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA: michael@0: case TLS_RSA_EXPORT_WITH_RC4_40_MD5: michael@0: case TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5: michael@0: testrsa_export = PR_TRUE; michael@0: } michael@0: if (!testrsa_export) michael@0: testrsa = PR_TRUE; michael@0: break; michael@0: case ssl_kea_ecdh: michael@0: if (strcmp(csdef.keaTypeName, "ECDHE") == 0) /* ephemeral? */ michael@0: testecdhe = PR_TRUE; michael@0: else michael@0: testecdh = PR_TRUE; michael@0: break; michael@0: case ssl_kea_dh: michael@0: /* this is actually DHE */ michael@0: default: michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: /* For each protocol try to derive and extract an MS. michael@0: * Failure of function any function except MS extract means michael@0: * continue with the next cipher test. Stop testing when the list is michael@0: * exhausted or when the first MS extract--not derive--fails. michael@0: */ michael@0: privKeytype = SECKEY_GetPrivateKeyType(srvPrivkey); michael@0: protocolmask &= SSL_CBP_SSL3|SSL_CBP_TLS1_0; michael@0: while (protocolmask) { michael@0: if (protocolmask & SSL_CBP_SSL3) { michael@0: isTLS = PR_FALSE; michael@0: protocolmask ^= SSL_CBP_SSL3; michael@0: } else { michael@0: isTLS = PR_TRUE; michael@0: protocolmask ^= SSL_CBP_TLS1_0; michael@0: } michael@0: michael@0: if (privKeytype == rsaKey && testrsa_export) { michael@0: if (PK11_GetPrivateModulusLen(srvPrivkey) > EXPORT_RSA_KEY_LENGTH) { michael@0: *pcanbypass = PR_FALSE; michael@0: rv = SECSuccess; michael@0: break; michael@0: } else michael@0: testrsa = PR_TRUE; michael@0: } michael@0: for (; privKeytype == rsaKey && testrsa; ) { michael@0: /* TLS_RSA */ michael@0: unsigned char rsaPmsBuf[SSL3_RSA_PMS_LENGTH]; michael@0: unsigned int outLen = 0; michael@0: CK_MECHANISM_TYPE target; michael@0: SECStatus irv; michael@0: michael@0: mechanism_array[0] = CKM_SSL3_PRE_MASTER_KEY_GEN; michael@0: mechanism_array[1] = CKM_RSA_PKCS; michael@0: michael@0: slot = PK11_GetBestSlotMultiple(mechanism_array, 2, pwArg); michael@0: if (slot == NULL) { michael@0: PORT_SetError(SSL_ERROR_TOKEN_SLOT_NOT_FOUND); michael@0: break; michael@0: } michael@0: michael@0: /* Generate the pre-master secret ... (client side) */ michael@0: version.major = 3 /*MSB(clientHelloVersion)*/; michael@0: version.minor = 0 /*LSB(clientHelloVersion)*/; michael@0: param.data = (unsigned char *)&version; michael@0: param.len = sizeof version; michael@0: pms = PK11_KeyGen(slot, CKM_SSL3_PRE_MASTER_KEY_GEN, ¶m, 0, pwArg); michael@0: PK11_FreeSlot(slot); michael@0: if (!pms) michael@0: break; michael@0: /* now wrap it */ michael@0: enc_pms.len = SECKEY_PublicKeyStrength(srvPubkey); michael@0: enc_pms.data = (unsigned char*)PORT_Alloc(enc_pms.len); michael@0: if (enc_pms.data == NULL) { michael@0: PORT_SetError(PR_OUT_OF_MEMORY_ERROR); michael@0: break; michael@0: } michael@0: irv = PK11_PubWrapSymKey(CKM_RSA_PKCS, srvPubkey, pms, &enc_pms); michael@0: if (irv != SECSuccess) michael@0: break; michael@0: PK11_FreeSymKey(pms); michael@0: pms = NULL; michael@0: /* now do the server side--check the triple bypass first */ michael@0: rv = PK11_PrivDecryptPKCS1(srvPrivkey, rsaPmsBuf, &outLen, michael@0: sizeof rsaPmsBuf, michael@0: (unsigned char *)enc_pms.data, michael@0: enc_pms.len); michael@0: /* if decrypt worked we're done with the RSA test */ michael@0: if (rv == SECSuccess) { michael@0: *pcanbypass = PR_TRUE; michael@0: break; michael@0: } michael@0: /* check for fallback to double bypass */ michael@0: target = isTLS ? CKM_TLS_MASTER_KEY_DERIVE michael@0: : CKM_SSL3_MASTER_KEY_DERIVE; michael@0: pms = PK11_PubUnwrapSymKey(srvPrivkey, &enc_pms, michael@0: target, CKA_DERIVE, 0); michael@0: rv = ssl_canExtractMS(pms, isTLS, PR_FALSE, pcanbypass); michael@0: if (rv == SECSuccess && *pcanbypass == PR_FALSE) michael@0: goto done; michael@0: break; michael@0: } michael@0: michael@0: /* Check for NULL to avoid double free. michael@0: * SECItem_FreeItem sets data NULL in secitem.c#265 michael@0: */ michael@0: if (enc_pms.data != NULL) { michael@0: SECITEM_FreeItem(&enc_pms, PR_FALSE); michael@0: } michael@0: #ifndef NSS_DISABLE_ECC michael@0: for (; (privKeytype == ecKey && ( testecdh || testecdhe)) || michael@0: (privKeytype == rsaKey && testecdhe); ) { michael@0: CK_MECHANISM_TYPE target; michael@0: SECKEYPublicKey *keapub = NULL; michael@0: SECKEYPrivateKey *keapriv; michael@0: SECKEYPublicKey *cpub = NULL; /* client's ephemeral ECDH keys */ michael@0: SECKEYPrivateKey *cpriv = NULL; michael@0: SECKEYECParams *pecParams = NULL; michael@0: michael@0: if (privKeytype == ecKey && testecdhe) { michael@0: /* TLS_ECDHE_ECDSA */ michael@0: pecParams = &srvPubkey->u.ec.DEREncodedParams; michael@0: } else if (privKeytype == rsaKey && testecdhe) { michael@0: /* TLS_ECDHE_RSA */ michael@0: ECName ec_curve; michael@0: int serverKeyStrengthInBits; michael@0: int signatureKeyStrength; michael@0: int requiredECCbits; michael@0: michael@0: /* find a curve of equivalent strength to the RSA key's */ michael@0: requiredECCbits = PK11_GetPrivateModulusLen(srvPrivkey); michael@0: if (requiredECCbits < 0) michael@0: break; michael@0: requiredECCbits *= BPB; michael@0: serverKeyStrengthInBits = srvPubkey->u.rsa.modulus.len; michael@0: if (srvPubkey->u.rsa.modulus.data[0] == 0) { michael@0: serverKeyStrengthInBits--; michael@0: } michael@0: /* convert to strength in bits */ michael@0: serverKeyStrengthInBits *= BPB; michael@0: michael@0: signatureKeyStrength = michael@0: SSL_RSASTRENGTH_TO_ECSTRENGTH(serverKeyStrengthInBits); michael@0: michael@0: if ( requiredECCbits > signatureKeyStrength ) michael@0: requiredECCbits = signatureKeyStrength; michael@0: michael@0: ec_curve = michael@0: ssl3_GetCurveWithECKeyStrength( michael@0: ssl3_GetSupportedECCurveMask(NULL), michael@0: requiredECCbits); michael@0: rv = ssl3_ECName2Params(NULL, ec_curve, &ecParams); michael@0: if (rv == SECFailure) { michael@0: break; michael@0: } michael@0: pecParams = &ecParams; michael@0: } michael@0: michael@0: if (testecdhe) { michael@0: /* generate server's ephemeral keys */ michael@0: keapriv = SECKEY_CreateECPrivateKey(pecParams, &keapub, NULL); michael@0: if (!keapriv || !keapub) { michael@0: if (keapriv) michael@0: SECKEY_DestroyPrivateKey(keapriv); michael@0: if (keapub) michael@0: SECKEY_DestroyPublicKey(keapub); michael@0: PORT_SetError(SEC_ERROR_KEYGEN_FAIL); michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: } else { michael@0: /* TLS_ECDH_ECDSA */ michael@0: keapub = srvPubkey; michael@0: keapriv = srvPrivkey; michael@0: pecParams = &srvPubkey->u.ec.DEREncodedParams; michael@0: } michael@0: michael@0: /* perform client side ops */ michael@0: /* generate a pair of ephemeral keys using server's parms */ michael@0: cpriv = SECKEY_CreateECPrivateKey(pecParams, &cpub, NULL); michael@0: if (!cpriv || !cpub) { michael@0: if (testecdhe) { michael@0: SECKEY_DestroyPrivateKey(keapriv); michael@0: SECKEY_DestroyPublicKey(keapub); michael@0: } michael@0: PORT_SetError(SEC_ERROR_KEYGEN_FAIL); michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: /* now do the server side */ michael@0: /* determine the PMS using client's public value */ michael@0: target = isTLS ? CKM_TLS_MASTER_KEY_DERIVE_DH michael@0: : CKM_SSL3_MASTER_KEY_DERIVE_DH; michael@0: pms = PK11_PubDeriveWithKDF(keapriv, cpub, PR_FALSE, NULL, NULL, michael@0: CKM_ECDH1_DERIVE, michael@0: target, michael@0: CKA_DERIVE, 0, CKD_NULL, NULL, NULL); michael@0: rv = ssl_canExtractMS(pms, isTLS, PR_TRUE, pcanbypass); michael@0: SECKEY_DestroyPrivateKey(cpriv); michael@0: SECKEY_DestroyPublicKey(cpub); michael@0: if (testecdhe) { michael@0: SECKEY_DestroyPrivateKey(keapriv); michael@0: SECKEY_DestroyPublicKey(keapub); michael@0: } michael@0: if (rv == SECSuccess && *pcanbypass == PR_FALSE) michael@0: goto done; michael@0: break; michael@0: } michael@0: /* Check for NULL to avoid double free. */ michael@0: if (ecParams.data != NULL) { michael@0: PORT_Free(ecParams.data); michael@0: ecParams.data = NULL; michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: if (pms) michael@0: PK11_FreeSymKey(pms); michael@0: } michael@0: michael@0: /* *pcanbypass has been set */ michael@0: rv = SECSuccess; michael@0: michael@0: done: michael@0: if (pms) michael@0: PK11_FreeSymKey(pms); michael@0: michael@0: /* Check for NULL to avoid double free. michael@0: * SECItem_FreeItem sets data NULL in secitem.c#265 michael@0: */ michael@0: if (enc_pms.data != NULL) { michael@0: SECITEM_FreeItem(&enc_pms, PR_FALSE); michael@0: } michael@0: #ifndef NSS_DISABLE_ECC michael@0: if (ecParams.data != NULL) { michael@0: PORT_Free(ecParams.data); michael@0: ecParams.data = NULL; michael@0: } michael@0: #endif /* NSS_DISABLE_ECC */ michael@0: michael@0: if (srvPubkey) { michael@0: SECKEY_DestroyPublicKey(srvPubkey); michael@0: srvPubkey = NULL; michael@0: } michael@0: michael@0: michael@0: return rv; michael@0: #endif /* NO_PKCS11_BYPASS */ michael@0: } michael@0: