/* Validate the cert chain and the private key for the material passed to matrixSslReadKeys. Good to catch any user certifiate errors as soon as possible */ static int32 verifyReadKeys(psPool_t *pool, sslKeys_t *keys) { #ifdef USE_CERT_PARSE psX509Cert_t *tmp; #endif if (keys->cert == NULL && keys->privKey == NULL) { return PS_SUCCESS; } /* Not allowed to have a certficate with no matching private key or private key with no cert to match with */ if (keys->cert != NULL && keys->privKey == NULL) { psTraceInfo("No private key given to matrixSslReadKeys cert\n"); return PS_CERT_AUTH_FAIL; } if (keys->privKey != NULL && keys->cert == NULL) { psTraceInfo("No cert given with private key to matrixSslReadKeys\n"); return PS_CERT_AUTH_FAIL; } #ifdef USE_CERT_PARSE /* If this is a chain, we can validate it here with psX509AuthenticateCert Don't check the error return code from this call because the chaining usage restrictions will test parent-most cert for self-signed. But we can look at 'authStatus' on all but the final cert to see if the rest looks good */ if (keys->cert != NULL && keys->cert->next != NULL) { psX509AuthenticateCert(pool, keys->cert, NULL); tmp = keys->cert; while (tmp->next != NULL) { if (tmp->authStatus != PS_TRUE) { psTraceInfo("Failed to authenticate cert chain\n"); return PS_CERT_AUTH_FAIL; } tmp = tmp->next; } } if (keys->privKey != NULL && keys->privKey->type == PS_RSA) { /* Testing the N member just as a sanity measure rather than attempting a full RSA crypt operation */ if (pstm_cmp(&(keys->privKey->key->rsa.N), &(keys->cert->publicKey.key->rsa.N)) != PSTM_EQ) { psTraceInfo("Private key doesn't match cert\n"); return PS_CERT_AUTH_FAIL; } } #endif /* USE_CERT_PARSE */ return PS_SUCCESS; }
/* User must re-enable rehandshaking before adding credits */ if (ssl->rehandshakeCount >= 0) { if ((ssl->rehandshakeCount + credits) < 0x8000) { ssl->rehandshakeCount += credits; } } } } #else /* !SSL_REHANDSHAKES_ENABLED */ int32 matrixSslEncodeRehandshake(ssl_t *ssl, sslKeys_t *keys, int32 (*certCb)(ssl_t *ssl, psX509Cert_t *cert, int32 alert), uint32 sessionOption, uint32 cipherSpec[], uint16 cipherSpecLen) { psTraceInfo("Rehandshaking is disabled. matrixSslEncodeRehandshake off\n"); return PS_FAILURE; }
/* matrixSslClose */ void matrixSslClose(void) { #ifdef USE_SERVER_SIDE_SSL int32 i; #ifdef USE_MULTITHREADING psLockMutex(&sessionTableLock); #endif /* USE_MULTITHREADING */ for (i = 0; i < SSL_SESSION_TABLE_SIZE; i++) { if (sessionTable[i].inUse > 1) { psTraceInfo("Warning: closing while session still in use\n"); } } memset(sessionTable, 0x0, sizeof(sslSessionEntry_t) * SSL_SESSION_TABLE_SIZE); #ifdef USE_MULTITHREADING psUnlockMutex(&sessionTableLock); psDestroyMutex(&sessionTableLock); psUnlockMutex(&prngLock); psDestroyMutex(&prngLock); #endif /* USE_MULTITHREADING */ #endif /* USE_SERVER_SIDE_SSL */ psCoreClose(); }
/* * Generates all key material. */ int32 sslDeriveKeys(ssl_t *ssl) { psDigestContext_t md5Ctx, sha1Ctx; unsigned char buf[MD5_HASH_SIZE + SHA1_HASH_SIZE]; unsigned char *tmp; uint32 i; /* If this session is resumed, we want to reuse the master secret to regenerate the key block with the new random values. */ if (ssl->flags & SSL_FLAGS_RESUMED) { goto skipPremaster; } /* master_secret = MD5(pre_master_secret + SHA('A' + pre_master_secret + ClientHello.random + ServerHello.random)) + MD5(pre_master_secret + SHA('BB' + pre_master_secret + ClientHello.random + ServerHello.random)) + MD5(pre_master_secret + SHA('CCC' + pre_master_secret + ClientHello.random + ServerHello.random)); */ tmp = ssl->sec.masterSecret; for (i = 0; i < 3; i++) { psSha1Init(&sha1Ctx); psSha1Update(&sha1Ctx, salt[i], i + 1); psSha1Update(&sha1Ctx, ssl->sec.premaster, ssl->sec.premasterSize); psSha1Update(&sha1Ctx, ssl->sec.clientRandom, SSL_HS_RANDOM_SIZE); psSha1Update(&sha1Ctx, ssl->sec.serverRandom, SSL_HS_RANDOM_SIZE); psSha1Final(&sha1Ctx, buf); psMd5Init(&md5Ctx); psMd5Update(&md5Ctx, ssl->sec.premaster, ssl->sec.premasterSize); psMd5Update(&md5Ctx, buf, SHA1_HASH_SIZE); psMd5Final(&md5Ctx, tmp); tmp += MD5_HASH_SIZE; } memset(buf, 0x0, MD5_HASH_SIZE + SHA1_HASH_SIZE); /* premaster is now allocated for DH reasons. Can free here */ psFree(ssl->sec.premaster); ssl->sec.premaster = NULL; ssl->sec.premasterSize = 0; skipPremaster: if (createKeyBlock(ssl, ssl->sec.clientRandom, ssl->sec.serverRandom, ssl->sec.masterSecret, SSL_HS_MASTER_SIZE) < 0) { psTraceInfo("Unable to create key block\n"); return PS_FAILURE; } return SSL_HS_MASTER_SIZE; }
/* Add a pre-shared key and ID to the static table in the first NULL spot */ int32 matrixSslLoadPsk(sslKeys_t *keys, unsigned char *key, uint32 keyLen, unsigned char *id, uint32 idLen) { psPsk_t *psk, *list; if (keys == NULL || key == NULL || id == NULL) { return PS_ARG_FAIL; } if (keyLen > SSL_PSK_MAX_KEY_SIZE) { psTraceIntInfo("Can't add PSK. Key too large: %d\n", keyLen); return PS_ARG_FAIL; } if (idLen > SSL_PSK_MAX_ID_SIZE) { psTraceIntInfo("Can't add PSK. Key ID too large: %d\n", idLen); return PS_ARG_FAIL; } if (keyLen < 1 || idLen < 1) { psTraceInfo("Can't add PSK. Both key and identity length must be >0\n"); return PS_ARG_FAIL; } if ((psk = psMalloc(keys->pool, sizeof(psPsk_t))) == NULL) { return PS_MEM_FAIL; } memset(psk, 0, sizeof(psPsk_t)); if ((psk->pskKey = psMalloc(keys->pool, keyLen)) == NULL) { psFree(psk); return PS_MEM_FAIL; } if ((psk->pskId = psMalloc(keys->pool, idLen)) == NULL) { psFree(psk->pskKey); psFree(psk); return PS_MEM_FAIL; } memcpy(psk->pskKey, key, keyLen); psk->pskLen = keyLen; memcpy(psk->pskId, id, idLen); psk->pskIdLen = idLen; if (keys->pskKeys == NULL) { keys->pskKeys = psk; } else { list = keys->pskKeys; while (list->next != NULL) { list = list->next; } list->next = psk; } return 0; }
/* Get the key from the pre-shared list based on id */ int32 matrixSslPskGetKey(ssl_t *ssl, unsigned char *id, uint32 idLen, unsigned char **key, uint32 *keyLen) { psPsk_t *psk; *key = NULL; psk = ssl->keys->pskKeys; if (psk == NULL) { psTraceInfo("No pre-shared keys loaded\n"); return PS_FAILURE; } if (idLen <= 0) { psTraceIntInfo("Bad PSK identity length: %d\n", idLen); return PS_ARG_FAIL; } /* Make sure the length matches as well */ while (psk) { if ((uint32)psk->pskIdLen == idLen) { if (memcmp(psk->pskId, id, idLen) == 0) { *key = psk->pskKey; *keyLen = psk->pskLen; return PS_SUCCESS; } } psk = psk->next; } psTraceInfo("Can't find PSK key from id\n"); return PS_SUCCESS; }
/* Get the id from the pre-shared key table based on given SSL session hint and hintLen are not currently used for the lookup */ int32 matrixSslPskGetKeyId(ssl_t *ssl, unsigned char **id, uint32 *idLen, char *hint, uint32 hintLen) { psPsk_t *psk; psk = ssl->keys->pskKeys; if (psk == NULL) { psTraceInfo("No pre-shared keys loaded\n"); return PS_FAILURE; } *id = psk->pskId; *idLen = psk->pskIdLen; return PS_SUCCESS; }
/* File should be a binary .p12 or .pfx */ int32 matrixSslLoadPkcs12(sslKeys_t *keys, unsigned char *certFile, unsigned char *importPass, int32 ipasslen, unsigned char *macPass, int32 mpasslen, int32 flags) { unsigned char *mPass; psPool_t *pool; int32 rc; if (keys == NULL) { return PS_ARG_FAIL; } pool = keys->pool; if (macPass == NULL) { mPass = importPass; mpasslen = ipasslen; } else { mPass = macPass; } if ((rc = psPkcs12Parse(pool, &keys->cert, &keys->privKey, certFile, flags, importPass, ipasslen, mPass, mpasslen)) < 0) { if (keys->cert) { psX509FreeCert(keys->cert); keys->cert = NULL; } if (keys->privKey) { psFreePubKey(keys->privKey); keys->privKey = NULL; } return rc; } ReorderCertChain(keys->cert); #ifdef USE_SERVER_SIDE_SSL if (verifyReadKeys(pool, keys) < PS_SUCCESS) { psTraceInfo("PKCS#12 parse success but material didn't validate\n"); psX509FreeCert(keys->cert); psFreePubKey(keys->privKey); keys->cert = NULL; keys->privKey = NULL; return PS_CERT_AUTH_FAIL; } #endif /* USE_SERVER_SIDE_SSL */ return PS_SUCCESS; }
/* Calls a user defined callback to allow for manual validation of the certificate. */ int32 matrixUserCertValidator(ssl_t *ssl, int32 alert, psX509Cert_t *subjectCert, sslCertCb_t certValidator) { int32 status; /* If there is no callback, return PS_SUCCESS because there has already been a test for the case where the certificate did NOT PASS pubkey test and a callback does not exist to manually handle. It is highly recommended that the user manually verify, but the cert material has internally authenticated and the user has implied that is sufficient enough. */ if (certValidator == NULL) { psTraceInfo("Internal cert auth passed. No user callback registered\n"); return PS_SUCCESS; } /* Finally, let the user know what the alert status is and give them the cert material to access. Any non-zero value in alert indicates there is a pending fatal alert. The user can look at authStatus members if they want to examine the cert that did not pass. */ if (alert == SSL_ALERT_NONE) { status = 0; } else { status = alert; } /* The user callback */ return certValidator(ssl, subjectCert, status); }
void matrixSslPrintHSDetails(ssl_t *ssl) { if (ssl->hsState == SSL_HS_DONE) { psTraceInfo("\n"); if (ssl->minVer == SSL3_MIN_VER) { psTraceInfo("SSL 3.0 "); } else if (ssl->minVer == TLS_MIN_VER) { psTraceInfo("TLS 1.0 "); } else if (ssl->minVer == TLS_1_1_MIN_VER) { psTraceInfo("TLS 1.1 "); } else if (ssl->minVer == TLS_1_2_MIN_VER) { psTraceInfo("TLS 1.2 "); } psTraceInfo("connection established: "); switch (ssl->cipher->ident) { case SSL_RSA_WITH_NULL_MD5: psTraceInfo("SSL_RSA_WITH_NULL_MD5\n"); break; case SSL_RSA_WITH_NULL_SHA: psTraceInfo("SSL_RSA_WITH_NULL_SHA\n"); break; case SSL_RSA_WITH_RC4_128_MD5: psTraceInfo("SSL_RSA_WITH_RC4_128_MD5\n"); break; case SSL_RSA_WITH_RC4_128_SHA: psTraceInfo("SSL_RSA_WITH_RC4_128_SHA\n"); break; case SSL_RSA_WITH_3DES_EDE_CBC_SHA: psTraceInfo("SSL_RSA_WITH_3DES_EDE_CBC_SHA\n"); break; case TLS_RSA_WITH_AES_128_CBC_SHA: psTraceInfo("TLS_RSA_WITH_AES_128_CBC_SHA\n"); break; case TLS_RSA_WITH_AES_256_CBC_SHA: psTraceInfo("TLS_RSA_WITH_AES_256_CBC_SHA\n"); break; default: psTraceIntInfo("!!!! DEFINE ME %d !!!!\n", ssl->cipher->ident); } //psTraceBytes(" Master Secret", ssl->sec.masterSecret, // SSL_HS_MASTER_SIZE); } return; }
/* New SSL protocol context This structure is associated with a single SSL connection. Each socket using SSL should be associated with a new SSL context. certBuf and privKey ARE NOT duplicated within the server context, in order to minimize memory usage with multiple simultaneous requests. They must not be deleted by caller until all server contexts using them are deleted. */ int32 matrixSslNewSession(ssl_t **ssl, sslKeys_t *keys, sslSessionId_t *session, int32 flags) { psPool_t *pool = NULL; ssl_t *lssl; /* First API level chance to make sure a user is not attempting to use client or server support that was not built into this library compile */ #ifndef USE_SERVER_SIDE_SSL if (flags & SSL_FLAGS_SERVER) { psTraceInfo("SSL_FLAGS_SERVER passed to matrixSslNewSession but MatrixSSL lib was not compiled with server support\n"); return PS_ARG_FAIL; } #endif #ifndef USE_CLIENT_SIDE_SSL if (!(flags & SSL_FLAGS_SERVER)) { psTraceInfo("SSL_FLAGS_SERVER was not passed to matrixSslNewSession but MatrixSSL was not compiled with client support\n"); return PS_ARG_FAIL; } #endif if (flags & SSL_FLAGS_CLIENT_AUTH) { psTraceInfo("SSL_FLAGS_CLIENT_AUTH passed to matrixSslNewSession but MatrixSSL was not compiled with USE_CLIENT_AUTH enabled\n"); return PS_ARG_FAIL; } if (flags & SSL_FLAGS_SERVER) { if (keys == NULL) { psTraceInfo("NULL keys parameter passed to matrixSslNewSession\n"); return PS_ARG_FAIL; } if (session != NULL) { psTraceInfo("Ignoring session parameter to matrixSslNewSession\n"); } } *ssl = lssl = psMalloc(pool, sizeof(ssl_t)); if (lssl == NULL) { psTraceInfo("Out of memory for ssl_t in matrixSslNewSession\n"); return PS_MEM_FAIL; } memset(lssl, 0x0, sizeof(ssl_t)); /* Data buffers */ lssl->outsize = SSL_DEFAULT_OUT_BUF_SIZE; lssl->outbuf = psMalloc(MATRIX_NO_POOL, lssl->outsize); if (lssl->outbuf == NULL) { psTraceInfo("Out of memory for outbuf in matrixSslNewSession\n"); psFree(lssl); return PS_MEM_FAIL; } lssl->insize = SSL_DEFAULT_IN_BUF_SIZE; lssl->inbuf = psMalloc(MATRIX_NO_POOL, lssl->insize); if (lssl->inbuf == NULL) { psTraceInfo("Out of memory for inbuf in matrixSslNewSession\n"); psFree(lssl->outbuf); psFree(lssl); return PS_MEM_FAIL; } lssl->sPool = pool; lssl->keys = keys; lssl->cipher = sslGetCipherSpec(lssl, SSL_NULL_WITH_NULL_NULL); sslActivateReadCipher(lssl); sslActivateWriteCipher(lssl); lssl->recordHeadLen = SSL3_HEADER_LEN; lssl->hshakeHeadLen = SSL3_HANDSHAKE_HEADER_LEN; if (flags & SSL_FLAGS_SERVER) { lssl->flags |= SSL_FLAGS_SERVER; /* Client auth can only be requested by server, not set by client */ if (flags & SSL_FLAGS_CLIENT_AUTH) { lssl->flags |= SSL_FLAGS_CLIENT_AUTH; } lssl->hsState = SSL_HS_CLIENT_HELLO; } else { /* Client is first to set protocol version information based on compile and/or the 'flags' parameter so header information in the handshake messages will be correctly set. */ #ifdef USE_TLS #ifndef DISABLE_TLS_1_0 lssl->majVer = TLS_MAJ_VER; lssl->minVer = TLS_MIN_VER; #endif #if defined(USE_TLS_1_1) && !defined(DISABLE_TLS_1_1) lssl->majVer = TLS_MAJ_VER; lssl->minVer = TLS_1_1_MIN_VER; lssl->flags |= SSL_FLAGS_TLS_1_1; #endif /* USE_TLS_1_1 */ if (lssl->majVer == 0) { /* USE_TLS enabled but all DISABLE_TLS versions are enabled so use SSLv3. Compile time tests would catch if no versions are enabled at all */ lssl->majVer = SSL3_MAJ_VER; lssl->minVer = SSL3_MIN_VER; } else { lssl->flags |= SSL_FLAGS_TLS; } #else /* USE_TLS */ lssl->majVer = SSL3_MAJ_VER; lssl->minVer = SSL3_MIN_VER; #endif /* USE_TLS */ lssl->hsState = SSL_HS_SERVER_HELLO; if (session != NULL && session->cipherId != SSL_NULL_WITH_NULL_NULL) { lssl->cipher = sslGetCipherSpec(lssl, session->cipherId); if (lssl->cipher == NULL) { psTraceInfo("Invalid session id to matrixSslNewSession\n"); } else { memcpy(lssl->sec.masterSecret, session->masterSecret, SSL_HS_MASTER_SIZE); lssl->sessionIdLen = SSL_MAX_SESSION_ID_SIZE; memcpy(lssl->sessionId, session->id, SSL_MAX_SESSION_ID_SIZE); } } lssl->sid = session; } lssl->err = SSL_ALERT_NONE; return PS_SUCCESS; }
static int32 matrixSslLoadKeyMaterialMem(sslKeys_t *keys, unsigned char *certBuf, int32 certLen, unsigned char *privBuf, int32 privLen, unsigned char *CAbuf, int32 CAlen, int32 privKeyType) { psPool_t *pool; int32 err, flags = 0; if (keys == NULL) { return PS_ARG_FAIL; } pool = keys->pool; /* Setting flags to store raw ASN.1 stream for SSL CERTIFICATE message use */ flags = CERT_STORE_UNPARSED_BUFFER; if (certBuf) { #ifdef USE_SERVER_SIDE_SSL if (keys->cert != NULL) { return PS_UNSUPPORTED_FAIL; } if ((err = psX509ParseCert(pool, certBuf, (uint32)certLen, &keys->cert, flags)) < 0) { psX509FreeCert(keys->cert); keys->cert = NULL; return err; } #else psTraceInfo("Ignoring certBuf in matrixSslReadKeysMem\n"); #endif /* USE_SERVER_SIDE_SSL */ } if (privBuf) { #ifdef USE_SERVER_SIDE_SSL if (keys->privKey != NULL) { return PS_UNSUPPORTED_FAIL; } if (privKeyType == PS_RSA) { if ((err = pkcs1ParsePrivBin(pool, privBuf, (uint32)privLen, &keys->privKey)) < 0) { #ifdef USE_PKCS8 /* Attempt a PKCS#8 but mem parse doesn't take password */ if ((err = pkcs8ParsePrivBin(pool, privBuf, (uint32)privLen, NULL, &keys->privKey)) < 0) { psX509FreeCert(keys->cert); keys->cert = NULL; return err; } #else psX509FreeCert(keys->cert); keys->cert = NULL; return err; #endif } } #else psTraceInfo("Ignoring privBuf in matrixSslReadKeysMem\n"); #endif /* USE_SERVER_SIDE_SSL */ } #ifdef USE_SERVER_SIDE_SSL if (verifyReadKeys(pool, keys) < PS_SUCCESS) { psX509FreeCert(keys->cert); psFreePubKey(keys->privKey); keys->privKey = NULL; keys->cert = NULL; return PS_CERT_AUTH_FAIL; } #endif /* USE_SERVER_SIDE_SSL */ /* Not necessary to store binary representations of CA certs */ flags &= ~CERT_STORE_UNPARSED_BUFFER; if (CAbuf) { #ifdef USE_CLIENT_SIDE_SSL if (keys->CAcerts != NULL) { return PS_UNSUPPORTED_FAIL; } if ((err = psX509ParseCert(pool, CAbuf, (uint32)CAlen, &keys->CAcerts, flags)) < 0) { #ifdef USE_SERVER_SIDE_SSL psFreePubKey(keys->privKey); psX509FreeCert(keys->cert); psX509FreeCert(keys->CAcerts); keys->privKey = NULL; keys->cert = keys->CAcerts = NULL; #endif return err; } #else psTraceInfo("Ignoring CAbuf in matrixSslReadKeysMem\n"); #endif /* USE_CLIENT_SIDE_SSL */ } return PS_SUCCESS; }
int32 sslActivateWriteCipher(ssl_t *ssl) { #ifdef USE_DTLS if (ssl->flags & SSL_FLAGS_DTLS) { if (ssl->retransmit == 0) { ssl->oencrypt = ssl->encrypt; ssl->ogenerateMac = ssl->generateMac; ssl->oenMacSize = ssl->enMacSize; ssl->oenNativeHmacSize = ssl->nativeEnMacSize; ssl->oenBlockSize = ssl->enBlockSize; ssl->oenIvSize = ssl->enIvSize; memcpy(ssl->owriteMAC, ssl->sec.writeMAC, ssl->enMacSize); memcpy(&ssl->oencryptCtx, &ssl->sec.encryptCtx, sizeof(psCipherContext_t)); memcpy(ssl->owriteIV, ssl->sec.writeIV, ssl->cipher->ivSize); } } #endif /* USE_DTLS */ ssl->encrypt = ssl->cipher->encrypt; ssl->generateMac = ssl->cipher->generateMac; ssl->nativeEnMacSize = ssl->cipher->macSize; if (ssl->extFlags.truncated_hmac) { if (ssl->cipher->macSize > 0) { /* Only for HMAC-based ciphers */ ssl->enMacSize = 10; } else { ssl->enMacSize = ssl->cipher->macSize; } } else { ssl->enMacSize = ssl->cipher->macSize; } ssl->enBlockSize = ssl->cipher->blockSize; ssl->enIvSize = ssl->cipher->ivSize; /* Reset the outgoing sequence number for the new suite */ memset(ssl->sec.seq, 0x0, sizeof(ssl->sec.seq)); if (ssl->cipher->ident != SSL_NULL_WITH_NULL_NULL) { ssl->flags |= SSL_FLAGS_WRITE_SECURE; #ifdef USE_TLS_1_2 if (ssl->enMacSize == 0) { /* Need a concept for AEAD read and write start times for the cases surrounding changeCipherSpec if moving from one suite to another */ ssl->flags |= SSL_FLAGS_AEAD_W; if (ssl->cipher->flags & CRYPTO_FLAGS_CHACHA) { ssl->flags &= ~SSL_FLAGS_NONCE_W; } else { ssl->flags |= SSL_FLAGS_NONCE_W; } } else { ssl->flags &= ~SSL_FLAGS_AEAD_W; ssl->flags &= ~SSL_FLAGS_NONCE_W; } #endif /* Copy the newly activated write keys into the live buffers */ memcpy(ssl->sec.writeMAC, ssl->sec.wMACptr, ssl->enMacSize); memcpy(ssl->sec.writeKey, ssl->sec.wKeyptr, ssl->cipher->keySize); memcpy(ssl->sec.writeIV, ssl->sec.wIVptr, ssl->cipher->ivSize); /* set up encrypt contexts */ if (ssl->cipher->init) { if (ssl->cipher->init(&(ssl->sec), INIT_ENCRYPT_CIPHER, ssl->cipher->keySize) < 0) { psTraceInfo("Unable to init write cipher suite\n"); return PS_FAILURE; } } } return PS_SUCCESS; }
/* Master secret generation if extended_master_secret extension is used */ int32_t tlsExtendedDeriveKeys(ssl_t *ssl) { unsigned char msSeed[SHA384_HASHLEN + LABEL_EXT_SIZE]; unsigned char hash[SHA384_HASHLEN]; uint32_t outLen; int32_t rc = PS_FAIL; #ifdef USE_DTLS if (ssl->flags & SSL_FLAGS_DTLS && ssl->retransmit == 1) { /* The keyblock is still valid from the first pass */ return SSL_HS_MASTER_SIZE; } #endif /* If this session is resumed, we should reuse the master_secret to regenerate the key block with the new random values. We should not be here regenerating the master_secret! */ if (ssl->extFlags.extended_master_secret == 0 || ssl->flags & SSL_FLAGS_RESUMED) { psTraceInfo("Invalid invokation of extended key derivation.\n"); return PS_FAIL; } extMasterSecretSnapshotHSHash(ssl, hash, &outLen); /* master_secret = PRF(pre_master_secret, "extended master secret", session_hash); */ memcpy(msSeed, LABEL_EXT_MASTERSEC, LABEL_EXT_SIZE); memcpy(msSeed + LABEL_EXT_SIZE, hash, outLen); #ifdef USE_TLS_1_2 if (ssl->flags & SSL_FLAGS_TLS_1_2) { if ((rc = prf2(ssl->sec.premaster, ssl->sec.premasterSize, msSeed, outLen + LABEL_EXT_SIZE, ssl->sec.masterSecret, SSL_HS_MASTER_SIZE, ssl->cipher->flags)) < 0) { return rc; } #ifndef USE_ONLY_TLS_1_2 } else { if ((rc = prf(ssl->sec.premaster, ssl->sec.premasterSize, msSeed, outLen + LABEL_EXT_SIZE, ssl->sec.masterSecret, SSL_HS_MASTER_SIZE)) < 0) { return rc; } #endif } #else if ((rc = prf(ssl->sec.premaster, ssl->sec.premasterSize, msSeed, outLen + LABEL_EXT_SIZE, ssl->sec.masterSecret, SSL_HS_MASTER_SIZE)) < 0) { return rc; } #endif #ifdef USE_DTLS if (ssl->flags & SSL_FLAGS_DTLS) { /* May need premaster for retransmits. DTLS will free this when handshake is known to be complete */ return genKeyBlock(ssl); } #endif /* USE_DTLS */ /* premaster is now allocated for DH reasons. Can free here */ psFree(ssl->sec.premaster, ssl->hsPool); ssl->sec.premaster = NULL; ssl->sec.premasterSize = 0; return genKeyBlock(ssl); }
/* Caller has received data from the network and is notifying the SSL layer */ int32 matrixSslReceivedData(ssl_t *ssl, uint32 bytes, unsigned char **ptbuf, uint32 *ptlen) { unsigned char *buf, *prevBuf; int32 rc, decodeRet, size, sanity, decodeErr; uint32 processed, start, len, reqLen; unsigned char alertLevel, alertDesc; unsigned char *p; if (!ssl || !ptbuf || !ptlen) { return PS_ARG_FAIL; } psAssert(ssl->outsize > 0 && ssl->outbuf != NULL); psAssert(ssl->insize > 0 && ssl->inbuf != NULL); *ptbuf = NULL; *ptlen = 0; ssl->inlen += bytes; if (ssl->inlen == 0) { return PS_SUCCESS; /* Nothing to do. Basically a poll */ } /* This is outside the loop b/c we may want to parse within inbuf later */ buf = ssl->inbuf; DECODE_MORE: /* Parameterized sanity check to avoid infinite loops */ if (matrixSslHandshakeIsComplete(ssl)) { /* Minimum possible record size once negotiated */ sanity = ssl->inlen / (SSL3_HEADER_LEN + MD5_HASH_SIZE); } else { /* Even with an SSLv2 hello, the sanity check will let 1 pass through */ sanity = ssl->inlen / (SSL3_HEADER_LEN + SSL3_HANDSHAKE_HEADER_LEN); } if (sanity-- < 0) { return PS_PROTOCOL_FAIL; /* We've tried to decode too many times */ } len = ssl->inlen; size = ssl->insize - (buf - ssl->inbuf); prevBuf = buf; decodeRet = matrixSslDecode(ssl, &buf, &len, size, &start, &reqLen, &decodeErr, &alertLevel, &alertDesc); /* Convenience for the cases that expect buf to have moved - calculate the number of encoded bytes that were decoded */ processed = buf - prevBuf; rc = PS_PROTOCOL_FAIL; switch (decodeRet) { case MATRIXSSL_SUCCESS: ssl->inlen -= processed; if (ssl->inlen > 0) { psAssert(buf > ssl->inbuf); /* Pack ssl->inbuf so there is immediate maximum room for potential outgoing data that needs to be written */ memmove(ssl->inbuf, buf, ssl->inlen); buf = ssl->inbuf; goto DECODE_MORE; /* More data in buffer to process */ } /* In this case, we've parsed a finished message and no additional data is available to parse. We let the client know the handshake is complete, which can be used as a trigger to begin for example a HTTP request. */ if (!(ssl->bFlags & BFLAG_HS_COMPLETE)) { if (matrixSslHandshakeIsComplete(ssl)) { ssl->bFlags |= BFLAG_HS_COMPLETE; #ifdef USE_CLIENT_SIDE_SSL matrixSslGetSessionId(ssl, ssl->sid); #endif /* USE_CLIENT_SIDE_SSL */ rc = MATRIXSSL_HANDSHAKE_COMPLETE; } else { rc = MATRIXSSL_REQUEST_RECV; /* Need to recv more handshake data */ } } else { #ifdef USE_DTLS rc = MATRIXSSL_REQUEST_RECV; /* Got FINISHED without CCS */ #else /* This is an error - we shouldn't get here */ #endif } break; #ifdef USE_DTLS case DTLS_RETRANSMIT: /* Only request a resend if last record in buffer */ ssl->inlen -= processed; if (ssl->inlen > 0) { psAssert(buf > ssl->inbuf); /* Pack ssl->inbuf so there is immediate maximum room for potential outgoing data that needs to be written */ memmove(ssl->inbuf, buf, ssl->inlen); buf = ssl->inbuf; goto DECODE_MORE; /* More data in buffer to process */ } /* Flight will be rebuilt when matrixDtlsGetOutdata is called while outbuf is empty. This is the return case where we are actually seeing a repeat handshake message so we know something was lost in flight. */ return MATRIXSSL_REQUEST_SEND; #endif case SSL_SEND_RESPONSE: #ifdef ENABLE_FALSE_START /* If FALSE START is supported, there may be APPLICATION_DATA directly following the FINISHED message, even though we haven't sent our CHANGE_CIPHER_SPEC or FINISHED message. This is signalled by buf having been moved forward, and our response being put directly into ssl->outbuf, rather than in buf (ssl->inbuf). Return a REQUEST_SEND so that the data in outbuf is flushed before the remaining data in ssl->inbuf is parsed. */ if ((ssl->flags & SSL_FLAGS_FALSE_START) && buf != prevBuf) { ssl->inlen -= processed; psAssert(ssl->inlen > 0); psAssert((uint32)ssl->inlen == start); psAssert(buf > ssl->inbuf); memmove(ssl->inbuf, buf, ssl->inlen); /* Pack ssl->inbuf */ buf = ssl->inbuf; return MATRIXSSL_REQUEST_SEND; } #endif /* This must be handshake data (or alert) or we'd be in PROCESS_DATA so there is no way there is anything left inside inbuf to process. ...so processed isn't valid because the output params are outbuf related and we simply reset inlen */ ssl->inlen = 0; /* If alert, close connection after sending */ if (alertDesc != SSL_ALERT_NONE) { ssl->bFlags |= BFLAG_CLOSE_AFTER_SENT; } psAssert(prevBuf == buf); psAssert(ssl->insize >= (int32)len); psAssert(start == 0); psAssert(buf == ssl->inbuf); if (ssl->outlen > 0) { /* If data's in outbuf, append inbuf. This is a corner case that can happen if application data is queued but then incoming data is processed and discovered to be a re-handshake request. matrixSslDecode will have constructed the response flight but we don't want to forget about the app data we haven't sent */ if (ssl->outlen + (int32)len > ssl->outsize) { if ((p = psRealloc(ssl->outbuf, ssl->outlen + len, ssl->bufferPool)) == NULL) { return PS_MEM_FAIL; } ssl->outbuf = p; ssl->outsize = ssl->outlen + len; } memcpy(ssl->outbuf + ssl->outlen, ssl->inbuf, len); ssl->outlen += len; } else { /* otherwise, swap inbuf and outbuf */ buf = ssl->outbuf; ssl->outbuf = ssl->inbuf; ssl->inbuf = buf; ssl->outlen = len; len = ssl->outsize; ssl->outsize = ssl->insize; ssl->insize = len; buf = ssl->inbuf; len = ssl->outlen; } rc = MATRIXSSL_REQUEST_SEND; /* We queued data to send out */ break; case MATRIXSSL_ERROR: if (decodeErr >= 0) { //printf("THIS SHOULD BE A NEGATIVE VALUE?\n"); } return decodeErr; /* Will be a negative value */ case SSL_ALERT: if (alertLevel == SSL_ALERT_LEVEL_FATAL) { psTraceIntInfo("Received FATAL alert %d.\n", alertDesc); } else { /* Closure notify is the normal case */ if (alertDesc == SSL_ALERT_CLOSE_NOTIFY) { psTraceInfo("Normal SSL closure alert\n"); } else { psTraceIntInfo("Received WARNING alert %d\n", alertDesc); } } /* Let caller access the 2 data bytes (severity and description) */ #ifdef USE_TLS_1_1 /* Been ignoring the explicit IV up to this final return point. */ if ((ssl->flags & SSL_FLAGS_READ_SECURE) && (ssl->flags & SSL_FLAGS_TLS_1_1) && (ssl->enBlockSize > 1)) { prevBuf += ssl->enBlockSize; } #endif /* USE_TLS_1_1 */ psAssert(len == 2); *ptbuf = prevBuf; *ptlen = len; ssl->inlen -= processed; return MATRIXSSL_RECEIVED_ALERT; case SSL_PARTIAL: if (reqLen > SSL_MAX_BUF_SIZE) { return PS_MEM_FAIL; } if (reqLen > (uint32)ssl->insize) { if ((p = psRealloc(ssl->inbuf, reqLen, ssl->bufferPool)) == NULL) { return PS_MEM_FAIL; } ssl->inbuf = p; ssl->insize = reqLen; buf = ssl->inbuf; /* Don't need to change inlen */ } rc = MATRIXSSL_REQUEST_RECV; /* Expecting more data */ break; /* We've got outgoing data that's larger than our buffer */ case SSL_FULL: if (reqLen > SSL_MAX_BUF_SIZE) { return PS_MEM_FAIL; } /* We balk if we get a large handshake message */ if (reqLen > SSL_MAX_PLAINTEXT_LEN && !matrixSslHandshakeIsComplete(ssl)) { if (reqLen > SSL_MAX_PLAINTEXT_LEN) { return PS_MEM_FAIL; } } /* Can't envision any possible case where there is remaining data in inbuf to process and are getting SSL_FULL. */ ssl->inlen = 0; /* Grow inbuf */ if (reqLen > (uint32)ssl->insize) { len = ssl->inbuf - buf; if ((p = psRealloc(ssl->inbuf, reqLen, ssl->bufferPool)) == NULL) { return PS_MEM_FAIL; } ssl->inbuf = p; ssl->insize = reqLen; buf = ssl->inbuf + len; /* Note we leave inlen untouched here */ } else { psTraceInfo("Encoding error. Possible wrong flight messagSize\n"); return PS_PROTOCOL_FAIL; /* error in our encoding */ } goto DECODE_MORE; case SSL_PROCESS_DATA: /* Possible we received a finished message and app data in the same flight. In this case, the caller is not notified that the handshake is complete, but rather is notified that there is application data to process. */ if (!(ssl->bFlags & BFLAG_HS_COMPLETE) && matrixSslHandshakeIsComplete(ssl)) { ssl->bFlags |= BFLAG_HS_COMPLETE; #ifdef USE_CLIENT_SIDE_SSL matrixSslGetSessionId(ssl, ssl->sid); #endif /* USE_CLIENT_SIDE_SSL */ } /* . prevbuf points to start of unencrypted data . buf points to start of any remaining unencrypted data . start is length of remaining encrypted data yet to decode . len is length of unencrypted data ready for user processing */ ssl->inlen -= processed; psAssert((uint32)ssl->inlen == start); /* Call user plaintext data handler */ #ifdef USE_TLS_1_1 /* Been ignoring the explicit IV up to this final return point. */ /* NOTE: This test has been on enBlockSize for a very long time but it looks like it should be on deBlockSize since this a decryption. Changed and added an assert to see if these ever don't match */ psAssert(ssl->enBlockSize == ssl->deBlockSize); if ((ssl->flags & SSL_FLAGS_READ_SECURE) && (ssl->flags & SSL_FLAGS_TLS_1_1) && (ssl->deBlockSize > 1)) { len -= ssl->deBlockSize; prevBuf += ssl->deBlockSize; } /* END enBlockSize to deBlockSize change */ #endif /* USE_TLS_1_1 */ *ptbuf = prevBuf; *ptlen = len; #ifdef USE_DTLS /* This flag is used in conjuction with flightDone in the buffer management API set to determine whether we are still in a handshake state for attempting flight resends. If we are getting app data we know for certain we are out of the hs states. Testing HandshakeComplete is not enough because you never know if the other side got FINISHED. */ if (ssl->flags & SSL_FLAGS_DTLS) { ssl->appDataExch = 1; } #endif #ifdef USE_ZLIB_COMPRESSION if (ssl->compression > 0) { return MATRIXSSL_APP_DATA_COMPRESSED; } #endif return MATRIXSSL_APP_DATA; } /* switch decodeRet */ if (ssl->inlen > 0 && (buf != ssl->inbuf)) { psAssert(0); } /* Shrink inbuf to default size once inlen < default size, and we aren't expecting any more data in the buffer. If SSL_PARTIAL, don't shrink the buffer, since we expect to fill it up shortly. */ if (decodeRet != SSL_PARTIAL) { revertToDefaultBufsize(ssl, SSL_INBUF); } return rc; }
int32_t matrixSslNewClientSession(ssl_t **ssl, const sslKeys_t *keys, sslSessionId_t *sid, const uint16_t cipherSpec[], uint8_t cipherSpecLen, sslCertCb_t certCb, const char *expectedName, tlsExtension_t *extensions, sslExtCb_t extCb, sslSessOpts_t *options) { ssl_t *lssl; psBuf_t tmp; uint32 len; int32 rc, i; if (!ssl) { return PS_ARG_FAIL; } if (cipherSpecLen > 0 && (cipherSpec == NULL || cipherSpec[0] == 0)) { return PS_ARG_FAIL; } if (options == NULL) { return PS_ARG_FAIL; } *ssl = NULL; lssl = NULL; /* Give priority to cipher suite if session id is provided and doesn't match */ if (cipherSpec != NULL && cipherSpec[0] != 0 && sid != NULL && sid->cipherId != 0) { rc = 1; for (i = 0; i < cipherSpecLen; i++) { if (cipherSpec[i] == sid->cipherId) { rc = 0; } } if (rc) { psTraceInfo("Explicit cipher suite will override session cache\n"); memset(sid->id, 0, SSL_MAX_SESSION_ID_SIZE); memset(sid->masterSecret, 0, SSL_HS_MASTER_SIZE); sid->cipherId = 0; } } if ((rc = matrixSslNewSession(&lssl, keys, sid, options)) < 0) { return rc; } lssl->userPtr = options->userPtr; #ifndef USE_ONLY_PSK_CIPHER_SUITE if (expectedName) { if (psX509ValidateGeneralName((char*)expectedName) < 0) { matrixSslDeleteSession(lssl); return rc; } rc = strlen(expectedName); lssl->expectedName = psMalloc(lssl->sPool, rc + 1); strcpy(lssl->expectedName, expectedName); } if (certCb) { matrixSslSetCertValidator(lssl, certCb); } #endif if (extCb) { lssl->extCb = extCb; } RETRY_HELLO: tmp.size = lssl->outsize; tmp.buf = tmp.start = tmp.end = lssl->outbuf; if ((rc = matrixSslEncodeClientHello(lssl, &tmp, cipherSpec, cipherSpecLen, &len, extensions, options)) < 0) { if (rc == SSL_FULL) { if ((tmp.buf = psRealloc(lssl->outbuf, len, lssl->bufferPool)) == NULL) { matrixSslDeleteSession(lssl); return PS_MEM_FAIL; } lssl->outbuf = tmp.buf; lssl->outsize = len; goto RETRY_HELLO; } else { matrixSslDeleteSession(lssl); return rc; } } psAssert(tmp.start == tmp.buf); lssl->outlen = tmp.end - tmp.start; *ssl = lssl; return MATRIXSSL_REQUEST_SEND; }
int32 matrixSslReEnableRehandshakes(ssl_t *ssl) { psTraceInfo("Rehandshaking is not compiled into library at all.\n"); return PS_FAILURE; }
/* Encode a CLIENT_HELLO or HELLO_REQUEST to re-handshake an existing connection. Can't "downgrade" the re-handshake. This means if keys or certCb are NULL we stick with whatever the session already has loaded. keys should be NULL if no change in key material is being made cipherSpec is only used by clients */ int32_t matrixSslEncodeRehandshake(ssl_t *ssl, sslKeys_t *keys, int32 (*certCb)(ssl_t *ssl, psX509Cert_t *cert, int32 alert), uint32 sessionOption, const uint16_t cipherSpec[], uint8_t cipherSpecLen) { sslBuf_t sbuf; int32 rc, i; uint32 reqLen, newLen; unsigned char *p; sslSessOpts_t options; /* Clear extFlags for rehandshakes */ ssl->extFlags.truncated_hmac = 0; ssl->extFlags.sni = 0; if (!ssl) { return PS_ARG_FAIL; } if (cipherSpecLen > 0 && (cipherSpec == NULL || cipherSpec[0] == 0)) { return PS_ARG_FAIL; } if (ssl->bFlags & BFLAG_CLOSE_AFTER_SENT) { return PS_PROTOCOL_FAIL; } psAssert(ssl->outsize > 0 && ssl->outbuf != NULL); #ifdef DISABLE_DTLS_CLIENT_CHANGE_CIPHER_FROM_GCM_TO_GCM #endif /* DISABLE_DTLS_CLIENT_CHANGE_CIPHER_FROM_GCM_TO_GCM */ #ifdef USE_ZLIB_COMPRESSION /* Re-handshakes are not currently supported for compressed sessions. */ if (ssl->compression > 0) { psTraceInfo("Re-handshakes not supported for compressed sessions\n"); return PS_UNSUPPORTED_FAIL; } #endif /* The only explicit option that can be passsed in is SSL_OPTION_FULL_HANDSHAKE to indicate no resumption is allowed */ if (sessionOption == SSL_OPTION_FULL_HANDSHAKE) { matrixSslSetSessionOption(ssl, sessionOption, NULL); } /* If the key material or cert callback are provided we have to assume it was intentional to "upgrade" the re-handshake and we force full handshake No big overhead calling SetSessionOption with FULL_HS multiple times. */ if (keys != NULL) { ssl->keys = keys; matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL); } #ifndef USE_ONLY_PSK_CIPHER_SUITE if (certCb != NULL) { matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL); #if defined(USE_CLIENT_AUTH) || defined(USE_CLIENT_SIDE_SSL) matrixSslSetCertValidator(ssl, certCb); #endif /* USE_CLIENT_AUTH || USE_CLIENT_SIDE_SSL */ #if defined(USE_CLIENT_AUTH) && defined(USE_SERVER_SIDE_SSL) /* If server, a certCb is an explicit flag to set client auth just as it is in matrixSslNewServerSession */ if (ssl->flags & SSL_FLAGS_SERVER) { matrixSslSetSessionOption(ssl, SSL_OPTION_ENABLE_CLIENT_AUTH, NULL); } #endif /* USE_CLIENT_AUTH && USE_SERVER_SIDE_SSL */ } #endif /* !USE_ONLY_PSK_CIPHER_SUITE */ /* If cipher spec is explicitly different from current, force a full handshake */ if (!(ssl->flags & SSL_FLAGS_SERVER)) { rc = 0; if (cipherSpecLen > 0) { rc = 1; for (i = 0; i < cipherSpecLen; i++) { if (cipherSpec[i] == ssl->cipher->ident) { rc = 0; } } } if (rc) { matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL); } } #ifdef USE_DTLS if (ssl->flags & SSL_FLAGS_DTLS) { /* Resend epoch should be brought up-to-date with new epoch */ ssl->resendEpoch[0] = ssl->epoch[0]; ssl->resendEpoch[1] = ssl->epoch[1]; ssl->msn = ssl->resendMsn = 0; } #endif /* USE_DTLS */ /* Options are set. Encode the HELLO message */ newLen = 0; L_REHANDSHAKE: if (ssl->flags & SSL_FLAGS_SERVER) { sbuf.buf = sbuf.start = sbuf.end = ssl->outbuf + ssl->outlen; sbuf.size = ssl->outsize - ssl->outlen; if ((rc = matrixSslEncodeHelloRequest(ssl, &sbuf, &reqLen)) < 0) { if (rc == SSL_FULL && newLen == 0) { newLen = ssl->outlen + reqLen; if (newLen < SSL_MAX_BUF_SIZE) { if ((p = psRealloc(ssl->outbuf, newLen, ssl->bufferPool)) == NULL){ return PS_MEM_FAIL; } ssl->outbuf = p; ssl->outsize = newLen; goto L_REHANDSHAKE; } } return rc; } } else { sbuf.buf = sbuf.start = sbuf.end = ssl->outbuf + ssl->outlen; sbuf.size = ssl->outsize - ssl->outlen; memset(&options, 0x0, sizeof(sslSessOpts_t)); #ifdef USE_ECC_CIPHER_SUITE options.ecFlags = ssl->ecInfo.ecFlags; #endif /* Use extended master secret if original connection used it */ if (ssl->extFlags.extended_master_secret == 1) { options.extendedMasterSecret = 1; ssl->extFlags.extended_master_secret = 0; } else { options.extendedMasterSecret = -1; } if ((rc = matrixSslEncodeClientHello(ssl, &sbuf, cipherSpec, cipherSpecLen, &reqLen, NULL, &options)) < 0) { if (rc == SSL_FULL && newLen == 0) { newLen = ssl->outlen + reqLen; if (newLen < SSL_MAX_BUF_SIZE) { if ((p = psRealloc(ssl->outbuf, newLen, ssl->bufferPool)) == NULL) { return PS_MEM_FAIL; } ssl->outbuf = p; ssl->outsize = newLen; goto L_REHANDSHAKE; } } return rc; } } ssl->outlen += sbuf.end - sbuf.start; return MATRIXSSL_SUCCESS; }
static int32 matrixSslLoadKeyMaterial(sslKeys_t *keys, const char *certFile, const char *privFile, const char *privPass, const char *CAfile, int32 privKeyType) { psPool_t *pool; int32 err, flags; if (keys == NULL) { return PS_ARG_FAIL; } pool = keys->pool; /* Setting flags to store raw ASN.1 stream for SSL CERTIFICATE message use */ flags = CERT_STORE_UNPARSED_BUFFER; if (certFile) { #ifdef USE_SERVER_SIDE_SSL if (keys->cert != NULL) { return PS_UNSUPPORTED_FAIL; } if ((err = psX509ParseCertFile(pool, (char*)certFile, &keys->cert, flags)) < 0) { return err; } #else psTraceStrInfo("Ignoring %s certFile in matrixSslReadKeys\n", (char*)certFile); #endif /* USE_SERVER_SIDE_SSL */ } /* Parse the private key file */ if (privFile) { #ifdef USE_SERVER_SIDE_SSL if (keys->privKey != NULL) { if (keys->cert) { psX509FreeCert(keys->cert); keys->cert = NULL; } return PS_UNSUPPORTED_FAIL; } if (privKeyType == PS_RSA) { if ((err = pkcs1ParsePrivFile(pool, (char*)privFile, (char*)privPass, &keys->privKey)) < 0) { if (keys->cert) { psX509FreeCert(keys->cert); keys->cert = NULL; } return err; } } #else psTraceStrInfo("Ignoring %s privFile in matrixSslReadKeys\n", (char*)privFile); #endif /* USE_SERVER_SIDE_SSL */ } #ifdef USE_SERVER_SIDE_SSL if (verifyReadKeys(pool, keys) < PS_SUCCESS) { psTraceInfo("Cert parse success but material didn't validate\n"); psX509FreeCert(keys->cert); psFreePubKey(keys->privKey); keys->cert = NULL; keys->privKey = NULL; return PS_CERT_AUTH_FAIL; } #endif /* USE_SERVER_SIDE_SSL */ /* Not necessary to store binary representations of CA certs */ flags &= ~CERT_STORE_UNPARSED_BUFFER; if (CAfile) { #ifdef USE_CLIENT_SIDE_SSL if (keys->CAcerts != NULL) { return PS_UNSUPPORTED_FAIL; } if ((err = psX509ParseCertFile(pool, (char*)CAfile, &keys->CAcerts, flags)) < 0) { #ifdef USE_SERVER_SIDE_SSL if (keys->cert) { psX509FreeCert(keys->cert); keys->cert = NULL; } if (keys->privKey) { psFreePubKey(keys->privKey); keys->privKey = NULL; } #endif /* USE_SERVER_SIDE_SSL */ return err; } #else psTraceStrInfo("Ignoring %s CAfile in matrixSslReadKeys\n", (char*)CAfile); #endif /* USE_CLIENT_SIDE_SSL */ } return PS_SUCCESS; }
/* Encode a CLIENT_HELLO or HELLO_REQUEST to re-handshake an existing connection. Can't "downgrade" the re-handshake. This means if keys or certCb are NULL we stick with whatever the session already has loaded. keys should be NULL if no change in key material is being made cipherSpec is only used by clients */ int32 matrixSslEncodeRehandshake(ssl_t *ssl, sslKeys_t *keys, int32 (*certCb)(ssl_t *ssl, psX509Cert_t *cert, int32 alert), uint32 sessionOption, uint32 cipherSpec[], uint16 cipherSpecLen) { sslBuf_t sbuf; int32 rc, i; uint32 reqLen, newLen; unsigned char *p; if (!ssl) { return PS_ARG_FAIL; } if (cipherSpecLen > 0 && (cipherSpec == NULL || cipherSpec[0] == 0)) { return PS_ARG_FAIL; } if (ssl->bFlags & BFLAG_CLOSE_AFTER_SENT) { return PS_PROTOCOL_FAIL; } psAssert(ssl->outsize > 0 && ssl->outbuf != NULL); #ifdef USE_ZLIB_COMPRESSION /* Re-handshakes are not currently supported for compressed sessions. */ if (ssl->compression > 0) { psTraceInfo("Re-handshakes not supported for compressed sessions\n"); return PS_UNSUPPORTED_FAIL; } #endif /* The only explicit option that can be passsed in is SSL_OPTION_FULL_HANDSHAKE to indicate no resumption is allowed */ if (sessionOption == SSL_OPTION_FULL_HANDSHAKE) { matrixSslSetSessionOption(ssl, sessionOption, NULL); } /* If the key material or cert callback are provided we have to assume it was intentional to "upgrade" the re-handshake and we force full handshake No big overhead calling SetSessionOption with FULL_HS multiple times. */ if (keys != NULL) { ssl->keys = keys; matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL); } #ifndef USE_ONLY_PSK_CIPHER_SUITE if (certCb != NULL) { matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL); #if defined(USE_CLIENT_AUTH) || defined(USE_CLIENT_SIDE_SSL) matrixSslSetCertValidator(ssl, (sslCertCb_t)certCb); #endif /* USE_CLIENT_AUTH || USE_CLIENT_SIDE_SSL */ #if defined(USE_CLIENT_AUTH) && defined(USE_SERVER_SIDE_SSL) /* If server, a certCb is an explicit flag to set client auth just as it is in matrixSslNewServerSession */ if (ssl->flags & SSL_FLAGS_SERVER) { matrixSslSetSessionOption(ssl, SSL_OPTION_ENABLE_CLIENT_AUTH, NULL); } #endif /* USE_CLIENT_AUTH && USE_SERVER_SIDE_SSL */ } #endif /* !USE_ONLY_PSK_CIPHER_SUITE */ /* If cipher spec is explicitly different from current, force a full handshake */ if (!(ssl->flags & SSL_FLAGS_SERVER)) { rc = 0; if (cipherSpecLen > 0) { rc = 1; for (i = 0; i < cipherSpecLen; i++) { if (cipherSpec[i] == ssl->cipher->ident) { rc = 0; } } } if (rc) { matrixSslSetSessionOption(ssl, SSL_OPTION_FULL_HANDSHAKE, NULL); } } /* Options are set. Encode the HELLO message */ newLen = 0; L_REHANDSHAKE: if (ssl->flags & SSL_FLAGS_SERVER) { sbuf.buf = sbuf.start = sbuf.end = ssl->outbuf + ssl->outlen; sbuf.size = ssl->outsize - ssl->outlen; if ((rc = matrixSslEncodeHelloRequest(ssl, &sbuf, &reqLen)) < 0) { if (rc == SSL_FULL && newLen == 0) { newLen = ssl->outlen + reqLen; if (newLen < SSL_MAX_BUF_SIZE) { if ((p = psRealloc(ssl->outbuf, newLen)) == NULL){ return PS_MEM_FAIL; } ssl->outbuf = p; ssl->outsize = newLen; goto L_REHANDSHAKE; } } return rc; } } else { sbuf.buf = sbuf.start = sbuf.end = ssl->outbuf + ssl->outlen; sbuf.size = ssl->outsize - ssl->outlen; if ((rc = matrixSslEncodeClientHello(ssl, &sbuf, cipherSpec, cipherSpecLen, &reqLen, NULL)) < 0) { if (rc == SSL_FULL && newLen == 0) { newLen = ssl->outlen + reqLen; if (newLen < SSL_MAX_BUF_SIZE) { if ((p = psRealloc(ssl->outbuf, newLen)) == NULL) { return PS_MEM_FAIL; } ssl->outbuf = p; ssl->outsize = newLen; goto L_REHANDSHAKE; } } return rc; } } ssl->outlen += sbuf.end - sbuf.start; return MATRIXSSL_SUCCESS; }
/* Cipher suites are chosen before they are activated with the ChangeCipherSuite message. Additionally, the read and write cipher suites are activated at different times in the handshake process. The following APIs activate the selected cipher suite callback functions. */ int32 sslActivateReadCipher(ssl_t *ssl) { ssl->decrypt = ssl->cipher->decrypt; ssl->verifyMac = ssl->cipher->verifyMac; ssl->nativeDeMacSize = ssl->cipher->macSize; if (ssl->extFlags.truncated_hmac) { if (ssl->cipher->macSize > 0) { /* Only for HMAC-based ciphers */ ssl->deMacSize = 10; } else { ssl->deMacSize = ssl->cipher->macSize; } } else { ssl->deMacSize = ssl->cipher->macSize; } ssl->deBlockSize = ssl->cipher->blockSize; ssl->deIvSize = ssl->cipher->ivSize; /* Reset the expected incoming sequence number for the new suite */ memset(ssl->sec.remSeq, 0x0, sizeof(ssl->sec.remSeq)); if (ssl->cipher->ident != SSL_NULL_WITH_NULL_NULL) { /* Sanity */ if (ssl->sec.rMACptr == NULL) { psTraceInfo("sslActivateReadCipher sanity fail\n"); return PS_FAILURE; } ssl->flags |= SSL_FLAGS_READ_SECURE; #ifdef USE_TLS_1_2 if (ssl->deMacSize == 0) { /* Need a concept for AEAD read and write start times for the cases surrounding changeCipherSpec if moving from one suite to another */ ssl->flags |= SSL_FLAGS_AEAD_R; if (ssl->cipher->flags & CRYPTO_FLAGS_CHACHA) { ssl->flags &= ~SSL_FLAGS_NONCE_R; } else { ssl->flags |= SSL_FLAGS_NONCE_R; } } else { ssl->flags &= ~SSL_FLAGS_AEAD_R; ssl->flags &= ~SSL_FLAGS_NONCE_R; } #endif /* Copy the newly activated read keys into the live buffers */ memcpy(ssl->sec.readMAC, ssl->sec.rMACptr, ssl->deMacSize); memcpy(ssl->sec.readKey, ssl->sec.rKeyptr, ssl->cipher->keySize); memcpy(ssl->sec.readIV, ssl->sec.rIVptr, ssl->cipher->ivSize); /* set up decrypt contexts */ if (ssl->cipher->init) { if (ssl->cipher->init(&(ssl->sec), INIT_DECRYPT_CIPHER, ssl->cipher->keySize) < 0) { psTraceInfo("Unable to initialize read cipher suite\n"); return PS_FAILURE; } } } return PS_SUCCESS; }