Ejemplo n.º 1
0
static int tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
				const unsigned char *sess_id, int sesslen,
				SSL_SESSION **psess)
	{
	SSL_SESSION *sess;
	unsigned char *sdec;
	const unsigned char *p;
	int slen, mlen, renew_ticket = 0;
	unsigned char tick_hmac[EVP_MAX_MD_SIZE];
	HMAC_CTX hctx;
	EVP_CIPHER_CTX ctx;
	SSL_CTX *tctx = s->initial_ctx;
	/* Need at least keyname + iv + some encrypted data */
	if (eticklen < 48)
		goto tickerr;
	/* Initialize session ticket encryption and HMAC contexts */
	HMAC_CTX_init(&hctx);
	EVP_CIPHER_CTX_init(&ctx);
	if (tctx->tlsext_ticket_key_cb)
		{
		unsigned char *nctick = (unsigned char *)etick;
		int rv = tctx->tlsext_ticket_key_cb(s, nctick, nctick + 16,
							&ctx, &hctx, 0);
		if (rv < 0)
			return -1;
		if (rv == 0)
			goto tickerr;
		if (rv == 2)
			renew_ticket = 1;
		}
	else
		{
		/* Check key name matches */
		if (memcmp(etick, tctx->tlsext_tick_key_name, 16))
			goto tickerr;
		HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16,
					tlsext_tick_md(), NULL);
		EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
				tctx->tlsext_tick_aes_key, etick + 16);
		}
	/* Attempt to process session ticket, first conduct sanity and
 	 * integrity checks on ticket.
 	 */
	mlen = HMAC_size(&hctx);
	eticklen -= mlen;
	/* Check HMAC of encrypted ticket */
	HMAC_Update(&hctx, etick, eticklen);
	HMAC_Final(&hctx, tick_hmac, NULL);
	HMAC_CTX_cleanup(&hctx);
	if (memcmp(tick_hmac, etick + eticklen, mlen))
		goto tickerr;
	/* Attempt to decrypt session data */
	/* Move p after IV to start of encrypted ticket, update length */
	p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx);
	eticklen -= 16 + EVP_CIPHER_CTX_iv_length(&ctx);
	sdec = OPENSSL_malloc(eticklen);
	if (!sdec)
		{
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
		}
	EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen);
	if (EVP_DecryptFinal(&ctx, sdec + slen, &mlen) <= 0)
		goto tickerr;
	slen += mlen;
	EVP_CIPHER_CTX_cleanup(&ctx);
	p = sdec;
		
	sess = d2i_SSL_SESSION(NULL, &p, slen);
	OPENSSL_free(sdec);
	if (sess)
		{
		/* The session ID if non-empty is used by some clients to
 		 * detect that the ticket has been accepted. So we copy it to
 		 * the session structure. If it is empty set length to zero
 		 * as required by standard.
 		 */
		if (sesslen)
			memcpy(sess->session_id, sess_id, sesslen);
		sess->session_id_length = sesslen;
		*psess = sess;
		s->tlsext_ticket_expected = renew_ticket;
		return 1;
		}
	/* If session decrypt failure indicate a cache miss and set state to
 	 * send a new ticket
 	 */
	tickerr:	
	s->tlsext_ticket_expected = 1;
	return 0;
	}
Ejemplo n.º 2
0
/* tls_decrypt_ticket attempts to decrypt a session ticket.
 *
 *   etick: points to the body of the session ticket extension.
 *   eticklen: the length of the session tickets extenion.
 *   sess_id: points at the session ID.
 *   sesslen: the length of the session ID.
 *   psess: (output) on return, if a ticket was decrypted, then this is set to
 *       point to the resulting session.
 *
 * Returns:
 *   -1: fatal error, either from parsing or decrypting the ticket.
 *    2: the ticket couldn't be decrypted.
 *    3: a ticket was successfully decrypted and *psess was set.
 *    4: same as 3, but the ticket needs to be renewed.
 */
static int
tls_decrypt_ticket(SSL *s, const unsigned char *etick, int eticklen,
    const unsigned char *sess_id, int sesslen, SSL_SESSION **psess)
{
	SSL_SESSION *sess;
	unsigned char *sdec;
	const unsigned char *p;
	int slen, mlen, renew_ticket = 0;
	unsigned char tick_hmac[EVP_MAX_MD_SIZE];
	HMAC_CTX hctx;
	EVP_CIPHER_CTX ctx;
	SSL_CTX *tctx = s->initial_ctx;

	/*
	 * The API guarantees EVP_MAX_IV_LENGTH bytes of space for
	 * the iv to tlsext_ticket_key_cb().  Since the total space
	 * required for a session cookie is never less than this,
	 * this check isn't too strict.  The exact check comes later.
	 */
	if (eticklen < 16 + EVP_MAX_IV_LENGTH)
		return 2;

	/* Initialize session ticket encryption and HMAC contexts */
	HMAC_CTX_init(&hctx);
	EVP_CIPHER_CTX_init(&ctx);
	if (tctx->internal->tlsext_ticket_key_cb) {
		unsigned char *nctick = (unsigned char *)etick;
		int rv = tctx->internal->tlsext_ticket_key_cb(s,
		    nctick, nctick + 16, &ctx, &hctx, 0);
		if (rv < 0) {
			HMAC_CTX_cleanup(&hctx);
			EVP_CIPHER_CTX_cleanup(&ctx);
			return -1;
		}
		if (rv == 0) {
			HMAC_CTX_cleanup(&hctx);
			EVP_CIPHER_CTX_cleanup(&ctx);
			return 2;
		}
		if (rv == 2)
			renew_ticket = 1;
	} else {
		/* Check key name matches */
		if (timingsafe_memcmp(etick,
		    tctx->internal->tlsext_tick_key_name, 16))
			return 2;
		HMAC_Init_ex(&hctx, tctx->internal->tlsext_tick_hmac_key,
		    16, tlsext_tick_md(), NULL);
		EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
		    tctx->internal->tlsext_tick_aes_key, etick + 16);
	}

	/*
	 * Attempt to process session ticket, first conduct sanity and
	 * integrity checks on ticket.
	 */
	mlen = HMAC_size(&hctx);
	if (mlen < 0) {
		HMAC_CTX_cleanup(&hctx);
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
	}

	/* Sanity check ticket length: must exceed keyname + IV + HMAC */
	if (eticklen <= 16 + EVP_CIPHER_CTX_iv_length(&ctx) + mlen) {
		HMAC_CTX_cleanup(&hctx);
		EVP_CIPHER_CTX_cleanup(&ctx);
		return 2;
	}
	eticklen -= mlen;

	/* Check HMAC of encrypted ticket */
	if (HMAC_Update(&hctx, etick, eticklen) <= 0 ||
	    HMAC_Final(&hctx, tick_hmac, NULL) <= 0) {
		HMAC_CTX_cleanup(&hctx);
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
	}

	HMAC_CTX_cleanup(&hctx);
	if (timingsafe_memcmp(tick_hmac, etick + eticklen, mlen)) {
		EVP_CIPHER_CTX_cleanup(&ctx);
		return 2;
	}

	/* Attempt to decrypt session data */
	/* Move p after IV to start of encrypted ticket, update length */
	p = etick + 16 + EVP_CIPHER_CTX_iv_length(&ctx);
	eticklen -= 16 + EVP_CIPHER_CTX_iv_length(&ctx);
	sdec = malloc(eticklen);
	if (sdec == NULL ||
	    EVP_DecryptUpdate(&ctx, sdec, &slen, p, eticklen) <= 0) {
		free(sdec);
		EVP_CIPHER_CTX_cleanup(&ctx);
		return -1;
	}
	if (EVP_DecryptFinal_ex(&ctx, sdec + slen, &mlen) <= 0) {
		free(sdec);
		EVP_CIPHER_CTX_cleanup(&ctx);
		return 2;
	}
	slen += mlen;
	EVP_CIPHER_CTX_cleanup(&ctx);
	p = sdec;

	sess = d2i_SSL_SESSION(NULL, &p, slen);
	free(sdec);
	if (sess) {
		/* The session ID, if non-empty, is used by some clients to
		 * detect that the ticket has been accepted. So we copy it to
		 * the session structure. If it is empty set length to zero
		 * as required by standard.
		 */
		if (sesslen)
			memcpy(sess->session_id, sess_id, sesslen);
		sess->session_id_length = sesslen;
		*psess = sess;
		if (renew_ticket)
			return 4;
		else
			return 3;
	}
	ERR_clear_error();
	/* For session parse failure, indicate that we need to send a new
	 * ticket. */
	return 2;
}
Ejemplo n.º 3
0
static int ssl_encrypt_ticket_with_cipher_ctx(SSL *ssl, CBB *out,
                                              const uint8_t *session_buf,
                                              size_t session_len) {
  int ret = 0;

  EVP_CIPHER_CTX ctx;
  EVP_CIPHER_CTX_init(&ctx);
  HMAC_CTX hctx;
  HMAC_CTX_init(&hctx);

  /* If the session is too long, emit a dummy value rather than abort the
   * connection. */
  static const size_t kMaxTicketOverhead =
      16 + EVP_MAX_IV_LENGTH + EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE;
  if (session_len > 0xffff - kMaxTicketOverhead) {
    static const char kTicketPlaceholder[] = "TICKET TOO LARGE";
    if (CBB_add_bytes(out, (const uint8_t *)kTicketPlaceholder,
                      strlen(kTicketPlaceholder))) {
      ret = 1;
    }
    goto err;
  }

  /* Initialize HMAC and cipher contexts. If callback present it does all the
   * work otherwise use generated values from parent ctx. */
  SSL_CTX *tctx = ssl->session_ctx;
  uint8_t iv[EVP_MAX_IV_LENGTH];
  uint8_t key_name[16];
  if (tctx->tlsext_ticket_key_cb != NULL) {
    if (tctx->tlsext_ticket_key_cb(ssl, key_name, iv, &ctx, &hctx,
                                   1 /* encrypt */) < 0) {
      goto err;
    }
  } else {
    if (!RAND_bytes(iv, 16) ||
        !EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL,
                            tctx->tlsext_tick_aes_key, iv) ||
        !HMAC_Init_ex(&hctx, tctx->tlsext_tick_hmac_key, 16, tlsext_tick_md(),
                      NULL)) {
      goto err;
    }
    OPENSSL_memcpy(key_name, tctx->tlsext_tick_key_name, 16);
  }

  uint8_t *ptr;
  if (!CBB_add_bytes(out, key_name, 16) ||
      !CBB_add_bytes(out, iv, EVP_CIPHER_CTX_iv_length(&ctx)) ||
      !CBB_reserve(out, &ptr, session_len + EVP_MAX_BLOCK_LENGTH)) {
    goto err;
  }

  size_t total = 0;
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
  OPENSSL_memcpy(ptr, session_buf, session_len);
  total = session_len;
#else
  int len;
  if (!EVP_EncryptUpdate(&ctx, ptr + total, &len, session_buf, session_len)) {
    goto err;
  }
  total += len;
  if (!EVP_EncryptFinal_ex(&ctx, ptr + total, &len)) {
    goto err;
  }
  total += len;
#endif
  if (!CBB_did_write(out, total)) {
    goto err;
  }

  unsigned hlen;
  if (!HMAC_Update(&hctx, CBB_data(out), CBB_len(out)) ||
      !CBB_reserve(out, &ptr, EVP_MAX_MD_SIZE) ||
      !HMAC_Final(&hctx, ptr, &hlen) ||
      !CBB_did_write(out, hlen)) {
    goto err;
  }

  ret = 1;

err:
  EVP_CIPHER_CTX_cleanup(&ctx);
  HMAC_CTX_cleanup(&hctx);
  return ret;
}