/** * @file tls13CipherSuite.c * @version $Format:%h%d$ * * Functions for TLS 1.3 ciphersuites. */ /* * Copyright (c) 2013-2018 Rambus Inc. * Copyright (c) PeerSec Networks, 2002-2011 * All Rights Reserved * * The latest version of this code is available at http://www.matrixssl.org * * This software is open source; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This General Public License does NOT permit incorporating this software * into proprietary programs. If you are unable to comply with the GPL, a * commercial license for this software may be purchased from Rambus at * http://www.rambus.com/ * * This program is distributed in WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * http://www.gnu.org/copyleft/gpl.html */ /******************************************************************************/ #include "matrixsslImpl.h" # ifdef USE_TLS_1_3 /* 5.3. Per-Record Nonce: The per-record nonce for the AEAD construction is formed as follows: 1. The 64-bit record sequence number is encoded in network byte order and padded to the left with zeros to iv_length. 2. The padded sequence number is XORed with the static client_write_iv or server_write_iv, depending on the role. */ static inline void tls13MakeWriteNonce(ssl_t *ssl, unsigned char nonceOut[12]) { psSize_t i; Memset(nonceOut, 0, 12); Memcpy(nonceOut + 4, ssl->sec.seq, 8); for (i = 0; i < 12; i++) { nonceOut[i] ^= ssl->sec.tls13WriteIv[i]; } } static inline void tls13MakeReadNonce(ssl_t *ssl, unsigned char nonceOut[12]) { psSize_t i; Memset(nonceOut, 0, 12); Memcpy(nonceOut + 4, ssl->sec.remSeq, 8); for (i = 0; i < 12; i++) { nonceOut[i] ^= ssl->sec.tls13ReadIv[i]; } } static inline void tls13MakeEncryptAad(ssl_t *ssl, unsigned char aadOut[5]) { aadOut[0] = SSL_RECORD_TYPE_APPLICATION_DATA; aadOut[1] = 0x03; aadOut[2] = 0x03; aadOut[3] = (ssl->outRecLen & 0xff00) >> 8; aadOut[4] = (ssl->outRecLen & 0xff); } static inline void tls13MakeDecryptAad(ssl_t *ssl, unsigned char aadOut[5]) { aadOut[0] = SSL_RECORD_TYPE_APPLICATION_DATA; aadOut[1] = 0x03; aadOut[2] = 0x03; aadOut[3] = (ssl->rec.len & 0xff00) >> 8; aadOut[4] = (ssl->rec.len & 0xff); } #ifdef CL_EncryptAuthTls13 /* Allocate and load Key and State Assets from CL local storage, so that this application doesn't have to care about memory management. */ static CL_RV tls13_aesgcm_assets(psAesGcm_t *gcm) { CL_AssetAllocateExInfo_t exinfo = {0}; CL_RV rv; exinfo.flags |= CL_ASSET_ALLOCATE_EX_LOCAL; rv = CL_AssetAllocateEx((CL_ALLOCATE_EXTRA_POLICY | CL_POLICY_ALGO_GCM_AES_ENCRYPT | CL_POLICY_ALGO_GCM_AES_DECRYPT), gcm->keylength, &exinfo, &gcm->multipart_gcm_key); if (rv == CLR_OK) { rv = CL_AssetLoadValue(gcm->multipart_gcm_key, gcm->key, gcm->keylength); } if (rv == CLR_OK) { rv = CL_AssetAllocateEx((CL_ALLOCATE_EXTRA_POLICY | CL_POLICY_FLAG_TEMPORARY | CL_POLICY_FLAG_EXPORTABLE), CL_ASSET_STATE_MIN_LENGTH, &exinfo, &gcm->multipart_gcm_state); } if (rv != CLR_OK) { CL_AssetFree(gcm->multipart_gcm_key); CL_AssetFree(gcm->multipart_gcm_state); } return rv; } #endif int32 csAesGcmInitTls13(sslSec_t *sec, int32 type, uint32 keysize) { int32 err = 0; #ifdef CL_EncryptAuthTls13 psAesGcm_t *gcm; #endif if (type == INIT_ENCRYPT_CIPHER) { Memset(&sec->encryptCtx.aesgcm, 0, sizeof(psAesGcm_t)); if ((err = psAesInitGCM(&sec->encryptCtx.aesgcm, sec->writeKey, keysize)) < 0) { return err; } #ifdef CL_EncryptAuthTls13 gcm = &sec->encryptCtx.aesgcm; gcm->algo = CL_ALGO_GCM_AES_ENCRYPT; gcm->tlsInfo = CL_Tls13ContextNew(); CL_Tls13ContextSetWriteIv(gcm->tlsInfo, sec->tls13WriteIv); CL_Tls13ContextSetSeq(gcm->tlsInfo, sec->seq); err = tls13_aesgcm_assets(gcm) != CLR_OK; #endif } else { Memset(&sec->decryptCtx.aesgcm, 0, sizeof(psAesGcm_t)); if ((err = psAesInitGCM(&sec->decryptCtx.aesgcm, sec->readKey, keysize)) < 0) { return err; } #ifdef CL_EncryptAuthTls13 gcm = &sec->decryptCtx.aesgcm; gcm->algo = CL_ALGO_GCM_AES_DECRYPT; gcm->tlsInfo = CL_Tls13ContextNew(); CL_Tls13ContextSetWriteIv(gcm->tlsInfo, sec->tls13ReadIv); CL_Tls13ContextSetSeq(gcm->tlsInfo, sec->remSeq); err = tls13_aesgcm_assets(gcm) != CLR_OK; #endif } return err; } static inline void psAesIncrSec(unsigned char *seq) { int i; for (i = (TLS_AEAD_SEQNB_LEN - 1); i >= 0; i--) { seq[i]++; if (seq[i] != 0) { break; } } } #ifdef CL_EncryptAuthTls13 /* XXX: consider dispatching here based on underlying FL version. See TODO above. */ psResSize_t csAesGcmEncryptTls13(void *pssl, unsigned char *pt, unsigned char *ct, uint32 ptLen) { ssl_t *ssl = pssl; psAesGcm_t *gcm; CL_DataOutPtr_t tag = ct + ptLen; CL_DataLen_t tagLen = 16; CL_DataLen_t aadLen = 5; unsigned char aadBuf[5]; CL_DataInPtr_t aad = (CL_DataInPtr_t)aadBuf; CL_RV rv; gcm = &ssl->sec.encryptCtx.aesgcm; tls13MakeEncryptAad(ssl, aadBuf); rv = CLS_EncryptAuthTls13(flps_getCLS(), gcm->multipart_gcm_key, gcm->multipart_gcm_state, gcm->tlsInfo, gcm->algo, aad, aadLen, pt, ptLen, ct, tag, tagLen); if (rv == CLR_OK) { /* encrypted; commit to packet, increment sequence (the encrypt did that for tlsInfo) */ /* XXX: maybe copy out the tlsInfo ... */ psAesIncrSec(ssl->sec.seq); psAssert(memcmp(ssl->sec.seq, gcm->tlsInfo->Seq, 8) == 0); } return rv == CLR_OK ? ptLen : PS_FAILURE; } /* inLen includes tag */ psResSize_t csAesGcmDecryptTls13(void *pssl, unsigned char *ct, unsigned char *pt, uint32 inLen) { ssl_t *ssl = pssl; psAesGcm_t *gcm; CL_DataOutPtr_t tag; CL_DataLen_t tagLen = 16; unsigned char aadBuf[5]; CL_DataInPtr_t aad = (CL_DataInPtr_t)aadBuf; CL_DataLen_t aadLen = 5; CL_RV rv; ssize_t ptLen; ptLen = (inLen - tagLen); if (ptLen <= 0) { return PS_LIMIT_FAIL; } tag = ct + ptLen; gcm = &ssl->sec.decryptCtx.aesgcm; if (!USING_TLS_1_3_AAD(ssl)) { aadLen = 0; aad = NULL; } else { tls13MakeDecryptAad(ssl, aadBuf); } /* Assume the caller has already verified ssl->sec.seq == ssl->sec.remSeq */ psAssert(memcmp(ssl->sec.remSeq, gcm->tlsInfo->Seq, 8) == 0); rv = CLS_DecryptAuthTls13(flps_getCLS(), gcm->multipart_gcm_key, gcm->multipart_gcm_state, gcm->tlsInfo, gcm->algo, aad, aadLen, ct, ptLen, tag, tagLen, pt); if (rv == CLR_OK) { /* OK, commit to packet, increment sequence (the decrypt did that for tlsInfo) */ psAesIncrSec(ssl->sec.remSeq); psAssert(memcmp(ssl->sec.remSeq, gcm->tlsInfo->Seq, 8) == 0); } return rv == CLR_OK ? ptLen : PS_FAILURE; } #else /* CL_EncryptAuthTls13 */ int32 csAesGcmEncryptTls13(void *ssl, unsigned char *pt, unsigned char *ct, uint32 ptLen) { ssl_t *lssl = ssl; psAesGcm_t *ctx; unsigned char nonce[12]; unsigned char aad[5]; if (ptLen == 0) { return PS_SUCCESS; } ctx = &lssl->sec.encryptCtx.aesgcm; tls13MakeWriteNonce(lssl, nonce); if (USING_TLS_1_3_AAD(lssl)) { tls13MakeEncryptAad(lssl, aad); psAesReadyGCM(ctx, nonce, aad, 5); } else { /* Before draft 25, no AAD was used. */ psAesReadyGCM(ctx, nonce, NULL, 0); } psAesEncryptGCM(ctx, pt, ct, ptLen); psAesGetGCMTag(ctx, 16, ct + ptLen); /* Normally HMAC would increment the sequence */ psAesIncrSec(lssl->sec.seq); #ifdef DEBUG_TLS_1_3_GCM psTraceBytes("csAesGcmEncryptTls13 output with tag", ct, ptLen + TLS_GCM_TAG_LEN); psTraceBytes("Encrypt AAD", aad, 5); #endif return ptLen; } int32 csAesGcmDecryptTls13(void *ssl, unsigned char *ct, unsigned char *pt, uint32 len) { ssl_t *lssl = ssl; psAesGcm_t *ctx; int32 ctLen, bytes; unsigned char nonce[12]; unsigned char aad[5]; ctLen = len - TLS_GCM_TAG_LEN; if (ctLen <= 0) { return PS_LIMIT_FAIL; } ctx = &lssl->sec.decryptCtx.aesgcm; tls13MakeReadNonce(lssl, nonce); if (USING_TLS_1_3_AAD(lssl)) { tls13MakeDecryptAad(lssl, aad); psAesReadyGCM(ctx, nonce, aad, 5); } else { /* Before draft 25, no AAD was used. */ psAesReadyGCM(ctx, nonce, NULL, 0); } if ((bytes = psAesDecryptGCM(ctx, ct, len, pt, ctLen)) < 0) { return -1; } psAesIncrSec(lssl->sec.remSeq); #ifdef DEBUG_TLS_1_3_GCM psTraceBytes("csAesGcmDecryptTls13 output with tag", ct, ctLen); psTraceBytes("Decrypt AAD", aad, 5); #endif return bytes; } #endif /* CL_EncryptAuthTls13 */ #if defined(USE_CHACHA20_POLY1305_IETF_CIPHER_SUITE) || defined(USE_CHACHA20_POLY1305_IETF) int32 csChacha20Poly1305IetfEncryptTls13(void *ssl, unsigned char *pt, unsigned char *ct, uint32 len) { ssl_t *lssl = ssl; psChacha20Poly1305Ietf_t *ctx; unsigned char nonce[TLS_AEAD_NONCE_MAXLEN]; unsigned char aad[5]; int32 ptLen; if (len == 0) { return PS_SUCCESS; } ptLen = len; ctx = &lssl->sec.encryptCtx.chacha20poly1305ietf; tls13MakeWriteNonce(lssl, nonce); if (USING_TLS_1_3_AAD(lssl)) { tls13MakeEncryptAad(lssl, aad); } # ifdef DEBUG_CHACHA20_POLY1305_IETF_CIPHER_SUITE psTraceInfo("Entering csChacha20Poly1305IetfEncrypt IETF\n"); # endif if (sizeof(lssl->sec.writeIV) < CHACHA20POLY1305_IETF_IV_FIXED_LENGTH) { return PS_LIMIT_FAIL; } if (sizeof(nonce) < CHACHA20POLY1305_IETF_IV_FIXED_LENGTH) { return PS_LIMIT_FAIL; } # ifdef DEBUG_CHACHA20_POLY1305_IETF_CIPHER_SUITE psTraceBytes("nonce", nonce, CHACHA20POLY1305_IETF_IV_FIXED_LENGTH); psTraceBytes("pt", pt, ptLen); # endif /* Perform encryption and authentication tag computation */ (void)psChacha20Poly1305IetfEncrypt( ctx, pt, ptLen, nonce, aad, 5, ct); # ifdef DEBUG_CHACHA20_POLY1305_IETF_CIPHER_SUITE psTraceBytes("ct", ct, ptLen); psTraceBytes("tag", ct + ptLen, TLS_CHACHA20_POLY1305_IETF_TAG_LEN); psTraceBytes("aad", aad, 5); # endif /* Normally HMAC would increment the sequence */ psAesIncrSec(lssl->sec.seq); return len; } int32 csChacha20Poly1305IetfDecryptTls13(void *ssl, unsigned char *ct, unsigned char *pt, uint32 len) { ssl_t *lssl = ssl; psChacha20Poly1305Ietf_t *ctx; int32 bytes; # ifdef DEBUG_CHACHA20_POLY1305_IETF_CIPHER_SUITE int32 ctLen; # endif unsigned char nonce[TLS_AEAD_NONCE_MAXLEN]; unsigned char aad[5]; ctx = &lssl->sec.decryptCtx.chacha20poly1305ietf; tls13MakeReadNonce(lssl, nonce); if (USING_TLS_1_3_AAD(lssl)) { tls13MakeDecryptAad(lssl, aad); } /* Check https://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-06 */ # ifdef DEBUG_CHACHA20_POLY1305_IETF_CIPHER_SUITE psTraceInfo("Entering csChacha20Poly1305IetfDecrypt IETF\n"); # endif if (sizeof(lssl->sec.readIV) < CHACHA20POLY1305_IETF_IV_FIXED_LENGTH) { return PS_LIMIT_FAIL; } if (sizeof(nonce) < CHACHA20POLY1305_IETF_IV_FIXED_LENGTH) { return PS_LIMIT_FAIL; } /* Update length of encrypted data: we have to remove tag's length */ if (len < TLS_CHACHA20_POLY1305_IETF_TAG_LEN) { return PS_LIMIT_FAIL; } # ifdef DEBUG_CHACHA20_POLY1305_IETF_CIPHER_SUITE ctLen = len - TLS_CHACHA20_POLY1305_IETF_TAG_LEN; psTraceBytes("nonce", nonce, CHACHA20POLY1305_IETF_IV_FIXED_LENGTH); psTraceBytes("ct", ct, ctLen); psTraceBytes("tag", ct + ctLen, TLS_CHACHA20_POLY1305_IETF_TAG_LEN); psTraceBytes("aad", aad, 5); # endif /* --- Check authentication tag and decrypt data ---// */ if ((bytes = psChacha20Poly1305IetfDecrypt(ctx, ct, len, nonce, aad, 5, pt)) < 0) { # ifdef DEBUG_CHACHA20_POLY1305_IETF_CIPHER_SUITE psTraceInfo("Decrypt NOK\n"); # endif return -1; } psAesIncrSec(lssl->sec.remSeq); return bytes + TLS_CHACHA20_POLY1305_IETF_TAG_LEN; } #endif /* DEBUG_CHACHA20_POLY1305_IETF_CIPHER_SUITE */ int32_t tls13GetCipherHmacAlg(ssl_t *ssl) { if (ssl->cipher->ident == 0) { return 0; } if (ssl->cipher->flags & CRYPTO_FLAGS_SM3) { return HMAC_SM3; } if (ssl->cipher->flags & CRYPTO_FLAGS_SHA3) { return HMAC_SHA384; } else { return HMAC_SHA256; } } psResSize_t tls13GetCipherHashSize(ssl_t *ssl) { return (psGetOutputBlockLength(tls13GetCipherHmacAlg(ssl))); } int32_t tls13CipherIdToHmacAlg(uint32_t cipherId) { switch(cipherId) { case TLS_AES_128_GCM_SHA256: case TLS_CHACHA20_POLY1305_SHA256: case TLS_AES_128_CCM_SHA256: case TLS_AES_128_CCM_8_SHA256: return HMAC_SHA256; case TLS_AES_256_GCM_SHA384: return HMAC_SHA384; case TLS_SM4_GCM_SM3: case TLS_SM4_CCM_SM3: return HMAC_SM3; } return 0; } psBool_t isTls13Ciphersuite(uint16_t suite) { switch (suite) { case TLS_AES_128_GCM_SHA256: case TLS_CHACHA20_POLY1305_SHA256: case TLS_AES_128_CCM_SHA256: case TLS_AES_128_CCM_8_SHA256: case TLS_AES_256_GCM_SHA384: case TLS_SM4_GCM_SM3: case TLS_SM4_CCM_SM3: return PS_TRUE; default: return PS_FALSE; } } # endif /* USE_TLS_1_3 */