You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by ma...@apache.org on 2017/11/16 03:25:27 UTC

[trafficserver] branch quic-latest updated: Use AEAD instead of FNV-1a

This is an automated email from the ASF dual-hosted git repository.

maskit pushed a commit to branch quic-latest
in repository https://gitbox.apache.org/repos/asf/trafficserver.git


The following commit(s) were added to refs/heads/quic-latest by this push:
     new 4232178  Use AEAD instead of FNV-1a
4232178 is described below

commit 4232178f07465b8fc7d59d7c42ed58757d64713d
Author: Masakazu Kitajo <ma...@apache.org>
AuthorDate: Wed Nov 15 10:29:34 2017 +0900

    Use AEAD instead of FNV-1a
---
 .gitignore                                         |   1 +
 iocore/net/P_QUICNetVConnection.h                  |  10 +-
 iocore/net/QUICNetVConnection.cc                   | 172 +++++++--------
 iocore/net/quic/Makefile.am                        |   5 +
 iocore/net/quic/QUICCrypto.cc                      | 244 ++++++---------------
 iocore/net/quic/QUICCrypto.h                       |  50 ++---
 iocore/net/quic/QUICCrypto_openssl.cc              |  74 ++++---
 iocore/net/quic/QUICHandshake.cc                   |  14 +-
 iocore/net/quic/QUICKeyGenerator.cc                | 105 +++++++++
 iocore/net/quic/QUICKeyGenerator.h                 |  75 +++++++
 .../net/quic/QUICKeyGenerator_openssl.cc           |  35 ++-
 iocore/net/quic/QUICPacket.cc                      |  88 ++++++--
 iocore/net/quic/QUICPacket.h                       |  15 +-
 iocore/net/quic/QUICTypes.cc                       |  14 --
 iocore/net/quic/QUICTypes.h                        |   8 +-
 iocore/net/quic/test/Makefile.am                   |  26 +++
 iocore/net/quic/test/test_QUICKeyGenerator.cc      | 107 +++++++++
 lib/ts/HKDF.cc                                     |  11 +-
 lib/ts/HKDF.h                                      |   2 +-
 lib/ts/HKDF_openssl.cc                             |  19 +-
 20 files changed, 649 insertions(+), 426 deletions(-)

diff --git a/.gitignore b/.gitignore
index 31be23b..e50a04b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -97,6 +97,7 @@ iocore/net/quic/test/test_QUICStream
 iocore/net/quic/test/test_QUICStreamState
 iocore/net/quic/test/test_QUICStreamManager
 iocore/net/quic/test/test_QUICTransportParameters
+iocore/net/quic/test/test_QUICKeyGenerator
 iocore/net/quic/test/test_QUICCrypto
 iocore/net/quic/test/test_QUICLossDetector
 iocore/net/quic/test/test_QUICTypeUtil
diff --git a/iocore/net/P_QUICNetVConnection.h b/iocore/net/P_QUICNetVConnection.h
index 431ff76..b59bc03 100644
--- a/iocore/net/P_QUICNetVConnection.h
+++ b/iocore/net/P_QUICNetVConnection.h
@@ -103,9 +103,10 @@ class SSLNextProtocolSet;
  *  v
  * state_handshake()
  *  | READ:
- *  |   _state_handshake_process_initial_client_packet()
- *  |   _state_handshake_process_client_cleartext_packet()
- *  |   _state_handshake_process_zero_rtt_protected_packet()
+ *  |   _state_handshake_process__packet()
+ *  |     _state_handshake_process_initial_client_packet()
+ *  |     _state_handshake_process_client_cleartext_packet()
+ *  |     _state_handshake_process_zero_rtt_protected_packet()
  *  | WRITE:
  *  |   _state_common_send_packet()
  *  v
@@ -236,6 +237,7 @@ private:
 
   QUICErrorUPtr _recv_and_ack(const uint8_t *payload, uint16_t size, QUICPacketNumber packet_numm);
 
+  QUICErrorUPtr _state_handshake_process_packet(QUICPacketUPtr packet);
   QUICErrorUPtr _state_handshake_process_initial_client_packet(QUICPacketUPtr packet);
   QUICErrorUPtr _state_handshake_process_client_cleartext_packet(QUICPacketUPtr packet);
   QUICErrorUPtr _state_handshake_process_zero_rtt_protected_packet(QUICPacketUPtr packet);
@@ -249,7 +251,7 @@ private:
   void _init_flow_control_params(const std::shared_ptr<const QUICTransportParameters> &local_tp,
                                  const std::shared_ptr<const QUICTransportParameters> &remote_tp);
   void _handle_error(QUICErrorUPtr error);
-  QUICPacketUPtr _dequeue_recv_packet();
+  QUICPacketUPtr _dequeue_recv_packet(QUICPacketCreationResult &result);
 
   QUICStatelessToken _token;
 };
diff --git a/iocore/net/QUICNetVConnection.cc b/iocore/net/QUICNetVConnection.cc
index 29833c6..48edd88 100644
--- a/iocore/net/QUICNetVConnection.cc
+++ b/iocore/net/QUICNetVConnection.cc
@@ -381,36 +381,15 @@ QUICNetVConnection::state_handshake(int event, Event *data)
 
   switch (event) {
   case QUIC_EVENT_PACKET_READ_READY: {
-    QUICPacketUPtr p = this->_dequeue_recv_packet();
-    if (!p) {
-      break;
-    }
-    net_activity(this, this_ethread());
-
-    switch (p->type()) {
-    case QUICPacketType::CLIENT_INITIAL: {
-      error = this->_state_handshake_process_initial_client_packet(std::move(p));
-      break;
-    }
-    case QUICPacketType::CLIENT_CLEARTEXT: {
-      error = this->_state_handshake_process_client_cleartext_packet(std::move(p));
-      break;
-    }
-    case QUICPacketType::ZERO_RTT_PROTECTED: {
-      error = this->_state_handshake_process_zero_rtt_protected_packet(std::move(p));
-      break;
-    }
-    case QUICPacketType::ONE_RTT_PROTECTED_KEY_PHASE_0:
-    case QUICPacketType::ONE_RTT_PROTECTED_KEY_PHASE_1:
-      // Postpone processing the packet
-      this->_quic_packet_recv_queue.push(std::move(p));
-      this_ethread()->schedule_imm_local(this, event);
-      break;
-    default:
-      error = QUICErrorUPtr(new QUICConnectionError(QUICTransErrorCode::INTERNAL_ERROR));
-      break;
+    QUICPacketCreationResult result;
+    QUICPacketUPtr packet = this->_dequeue_recv_packet(result);
+    if (result == QUICPacketCreationResult::NOT_READY) {
+      error = QUICErrorUPtr(new QUICNoError());
+    } else if (result == QUICPacketCreationResult::FAILED) {
+      error = QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::TLS_FATAL_ALERT_GENERATED));
+    } else {
+      error = this->_state_handshake_process_packet(std::move(packet));
     }
-
     break;
   }
   case QUIC_EVENT_PACKET_WRITE_READY: {
@@ -640,6 +619,27 @@ QUICNetVConnection::largest_acked_packet_number()
 }
 
 QUICErrorUPtr
+QUICNetVConnection::_state_handshake_process_packet(QUICPacketUPtr packet)
+{
+  QUICErrorUPtr error = QUICErrorUPtr(new QUICNoError());
+  switch (packet->type()) {
+  case QUICPacketType::CLIENT_INITIAL:
+    error = this->_state_handshake_process_initial_client_packet(std::move(packet));
+    break;
+  case QUICPacketType::CLIENT_CLEARTEXT:
+    error = this->_state_handshake_process_client_cleartext_packet(std::move(packet));
+    break;
+  case QUICPacketType::ZERO_RTT_PROTECTED:
+    error = this->_state_handshake_process_zero_rtt_protected_packet(std::move(packet));
+    break;
+  default:
+    error = QUICErrorUPtr(new QUICConnectionError(QUICTransErrorCode::INTERNAL_ERROR));
+    break;
+  }
+  return error;
+}
+
+QUICErrorUPtr
 QUICNetVConnection::_state_handshake_process_initial_client_packet(QUICPacketUPtr packet)
 {
   if (packet->size() < MINIMUM_INITIAL_CLIENT_PACKET_SIZE) {
@@ -651,23 +651,17 @@ QUICNetVConnection::_state_handshake_process_initial_client_packet(QUICPacketUPt
   // Start handshake
   QUICErrorUPtr error = this->_handshake_handler->start(packet.get(), &this->_packet_factory);
   if (this->_handshake_handler->is_version_negotiated()) {
-    // Check integrity (QUIC-TLS-04: 6.1. Integrity Check Processing)
-    if (packet->has_valid_fnv1a_hash()) {
-      bool should_send_ack;
-      error = this->_frame_dispatcher->receive_frames(packet->payload(), packet->payload_size(), should_send_ack);
-      if (error->cls != QUICErrorClass::NONE) {
-        return error;
-      }
-      error = this->_local_flow_controller->update(this->_stream_manager->total_offset_received());
-      Debug("quic_flow_ctrl", "Connection [%" PRIx64 "] [LOCAL] %" PRIu64 "/%" PRIu64,
-            static_cast<uint64_t>(this->_quic_connection_id), this->_local_flow_controller->current_offset(),
-            this->_local_flow_controller->current_limit());
-      if (error->cls != QUICErrorClass::NONE) {
-        return error;
-      }
-    } else {
-      DebugQUICCon("Invalid FNV-1a hash value");
-      // Discard the packet
+    bool should_send_ack;
+    error = this->_frame_dispatcher->receive_frames(packet->payload(), packet->payload_size(), should_send_ack);
+    if (error->cls != QUICErrorClass::NONE) {
+      return error;
+    }
+    error = this->_local_flow_controller->update(this->_stream_manager->total_offset_received());
+    Debug("quic_flow_ctrl", "Connection [%" PRIx64 "] [LOCAL] %" PRIu64 "/%" PRIu64,
+          static_cast<uint64_t>(this->_quic_connection_id), this->_local_flow_controller->current_offset(),
+          this->_local_flow_controller->current_limit());
+    if (error->cls != QUICErrorClass::NONE) {
+      return error;
     }
   }
   return error;
@@ -676,23 +670,12 @@ QUICNetVConnection::_state_handshake_process_initial_client_packet(QUICPacketUPt
 QUICErrorUPtr
 QUICNetVConnection::_state_handshake_process_client_cleartext_packet(QUICPacketUPtr packet)
 {
-  QUICErrorUPtr error = QUICErrorUPtr(new QUICNoError());
-
-  // The payload of this packet contains STREAM frames and could contain PADDING and ACK frames
-  if (packet->has_valid_fnv1a_hash()) {
-    error = this->_recv_and_ack(packet->payload(), packet->payload_size(), packet->packet_number());
-  } else {
-    DebugQUICCon("Invalid FNV-1a hash value");
-    // Discard the packet
-  }
-  return error;
+  return this->_recv_and_ack(packet->payload(), packet->payload_size(), packet->packet_number());
 }
 
 QUICErrorUPtr
 QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(QUICPacketUPtr packet)
 {
-  // TODO: Decrypt the packet
-  // decrypt(payload, p);
   // TODO: Not sure what we have to do
   return QUICErrorUPtr(new QUICNoError());
 }
@@ -700,29 +683,19 @@ QUICNetVConnection::_state_handshake_process_zero_rtt_protected_packet(QUICPacke
 QUICErrorUPtr
 QUICNetVConnection::_state_connection_established_process_packet(QUICPacketUPtr packet)
 {
-  // TODO: fix size
-  size_t max_plain_txt_len = 2048;
-  ats_unique_buf plain_txt = ats_unique_malloc(max_plain_txt_len);
-  size_t plain_txt_len     = 0;
-
-  if (this->_crypto->decrypt(plain_txt.get(), plain_txt_len, max_plain_txt_len, packet->payload(), packet->payload_size(),
-                             packet->packet_number(), packet->header(), packet->header_size(), packet->key_phase())) {
-    DebugQUICCon("Decrypt Packet, pkt_num: %" PRIu64 ", header_len: %hu, payload_len: %zu", packet->packet_number(),
-                 packet->header_size(), plain_txt_len);
-    return this->_recv_and_ack(plain_txt.get(), plain_txt_len, packet->packet_number());
-  } else {
-    DebugQUICCon("Decrypt Error");
-    return QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::TLS_FATAL_ALERT_GENERATED));
-  }
+  return this->_recv_and_ack(packet->payload(), packet->payload_size(), packet->packet_number());
 }
 
 QUICErrorUPtr
 QUICNetVConnection::_state_common_receive_packet()
 {
   QUICErrorUPtr error = QUICErrorUPtr(new QUICNoError());
-  QUICPacketUPtr p    = this->_dequeue_recv_packet();
-  if (!p) {
-    return error;
+  QUICPacketCreationResult result;
+  QUICPacketUPtr p    = this->_dequeue_recv_packet(result);
+  if (result == QUICPacketCreationResult::FAILED) {
+    return QUICConnectionErrorUPtr(new QUICConnectionError(QUICTransErrorCode::TLS_FATAL_ALERT_GENERATED));
+  } else if (result == QUICPacketCreationResult::NOT_READY) {
+    return QUICErrorUPtr(new QUICNoError());
   }
   net_activity(this, this_ethread());
 
@@ -964,32 +937,39 @@ QUICNetVConnection::_handle_error(QUICErrorUPtr error)
 }
 
 QUICPacketUPtr
-QUICNetVConnection::_dequeue_recv_packet()
+QUICNetVConnection::_dequeue_recv_packet(QUICPacketCreationResult &result)
 {
   QUICPacketUPtr quic_packet = QUICPacketUPtr(nullptr, &QUICPacketDeleter::delete_null_packet);
   UDPPacket *udp_packet      = this->_packet_recv_queue.dequeue();
-  if (udp_packet) {
-    ats_unique_buf pkt = ats_unique_malloc(udp_packet->getPktLength());
-    IOBufferBlock *b   = udp_packet->getIOBlockChain();
-    size_t written     = 0;
-    while (b) {
-      memcpy(pkt.get() + written, b->buf(), b->read_avail());
-      written += b->read_avail();
-      b = b->next.get();
-    }
-    udp_packet->free();
-    quic_packet = QUICPacketFactory::create(std::move(pkt), written, this->largest_received_packet_number());
+  if (!udp_packet) {
+    result = QUICPacketCreationResult::NOT_READY;
+    return quic_packet;
   }
+  net_activity(this, this_ethread());
 
-  if (this->_quic_packet_recv_queue.size() > 0) {
-    QUICPacketUPtr p = std::move(this->_quic_packet_recv_queue.front());
-    this->_quic_packet_recv_queue.pop();
-    this->_quic_packet_recv_queue.push(std::move(quic_packet));
-    quic_packet.reset(p.release());
-  }
-  if (quic_packet) {
-    DebugQUICCon("type=%s pkt_num=%" PRIu64 " size=%u", QUICDebugNames::packet_type(quic_packet->type()),
-                 quic_packet->packet_number(), quic_packet->size());
+  // Create a QUIC packet
+  ats_unique_buf pkt = ats_unique_malloc(udp_packet->getPktLength());
+  IOBufferBlock *b   = udp_packet->getIOBlockChain();
+  size_t written     = 0;
+  while (b) {
+    memcpy(pkt.get() + written, b->buf(), b->read_avail());
+    written += b->read_avail();
+    b = b->next.get();
+  }
+  quic_packet = this->_packet_factory.create(std::move(pkt), written, this->largest_received_packet_number(), result);
+  if (result == QUICPacketCreationResult::NOT_READY) {
+    DebugQUICCon("Not ready to decrypt the packet");
+    // Retry later
+    this->_packet_recv_queue.enqueue(udp_packet);
+    this_ethread()->schedule_imm_local(this, QUIC_EVENT_PACKET_READ_READY);
+  } else {
+    udp_packet->free();
+    if (result == QUICPacketCreationResult::SUCCESS) {
+      DebugQUICCon("type=%s pkt_num=%" PRIu64 " size=%u", QUICDebugNames::packet_type(quic_packet->type()), quic_packet->packet_number(), quic_packet->size());
+    } else {
+      DebugQUICCon("Failed to decrypt the packet");
+    }
   }
+
   return quic_packet;
 }
diff --git a/iocore/net/quic/Makefile.am b/iocore/net/quic/Makefile.am
index 3fc2585..62df5dd 100644
--- a/iocore/net/quic/Makefile.am
+++ b/iocore/net/quic/Makefile.am
@@ -36,8 +36,10 @@ noinst_LIBRARIES = libquic.a
 
 if OPENSSL_IS_BORINGSSL
 QUICCrypto_impl = QUICCrypto_boringssl.cc
+QUICKeyGenerator_impl = QUICKeyGenerator_boringssl.cc
 else
 QUICCrypto_impl = QUICCrypto_openssl.cc
+QUICKeyGenerator_impl = QUICKeyGenerator_openssl.cc
 endif
 
 libquic_a_SOURCES = \
@@ -56,6 +58,9 @@ libquic_a_SOURCES = \
   QUICHandshake.cc \
   QUICCrypto.cc \
   $(QUICCrypto_impl) \
+  QUICKeyGenerator.cc \
+  $(QUICKeyGenerator_impl) \
+  QUICKeyGenerator.cc \
   QUICTransportParameters.cc \
   QUICAckFrameCreator.cc \
   QUICConfig.cc \
diff --git a/iocore/net/quic/QUICCrypto.cc b/iocore/net/quic/QUICCrypto.cc
index 3c6446d..ec020a8 100644
--- a/iocore/net/quic/QUICCrypto.cc
+++ b/iocore/net/quic/QUICCrypto.cc
@@ -34,44 +34,41 @@
 
 constexpr static char tag[] = "quic_crypto";
 
-// constexpr static ts::StringView _exporter_label_0_rtt("EXPORTER-QUIC 0-RTT Secret", ts::StringView::literal);
-constexpr static ts::string_view exporter_label_client_1_rtt("EXPORTER-QUIC client 1-RTT Secret"_sv);
-constexpr static ts::string_view exporter_label_server_1_rtt("EXPORTER-QUIC server 1-RTT Secret"_sv);
-
-// [quic-tls draft-05] "tls13 " + Label
-// constexpr static ts::StringView expand_label_client_1_rtt("tls13 QUIC client 1-RTT secret", ts::StringView::literal);
-// constexpr static ts::StringView expand_label_server_1_rtt("tls13 QUIC server 1-RTT secret", ts::StringView::literal);
-constexpr static ts::string_view expand_label_key("tls13 key"_sv);
-constexpr static ts::string_view expand_label_iv("tls13 iv"_sv);
-
 //
 // QUICPacketProtection
 //
 
 QUICPacketProtection::~QUICPacketProtection()
 {
-  delete this->_phase_0_key;
-  delete this->_phase_1_key;
 }
 
 void
-QUICPacketProtection::set_key(KeyMaterial *km, QUICKeyPhase phase)
+QUICPacketProtection::set_key(std::unique_ptr<KeyMaterial>km, QUICKeyPhase phase)
 {
   this->_key_phase = phase;
-  if (phase == QUICKeyPhase::PHASE_0) {
-    this->_phase_0_key = km;
-  } else {
-    this->_phase_1_key = km;
+  switch(phase) {
+  case QUICKeyPhase::PHASE_0:
+    this->_phase_0_key = std::move(km);
+    break;
+  case QUICKeyPhase::PHASE_1:
+    this->_phase_1_key = std::move(km);
+    break;
+  case QUICKeyPhase::CLEARTEXT:
+    this->_cleartext_key = std::move(km);
+    break;
   }
 }
 
-const KeyMaterial *
+const KeyMaterial&
 QUICPacketProtection::get_key(QUICKeyPhase phase) const
 {
-  if (phase == QUICKeyPhase::PHASE_0) {
-    return this->_phase_0_key;
-  } else {
-    return this->_phase_1_key;
+  switch(phase) {
+  case QUICKeyPhase::PHASE_0:
+    return *this->_phase_0_key;
+  case QUICKeyPhase::PHASE_1:
+    return *this->_phase_1_key;
+  case QUICKeyPhase::CLEARTEXT:
+    return *this->_cleartext_key;
   }
 }
 
@@ -100,7 +97,6 @@ QUICCrypto::QUICCrypto(SSL *ssl, NetVConnectionContext_t nvc_ctx) : _ssl(ssl), _
 
 QUICCrypto::~QUICCrypto()
 {
-  delete this->_hkdf;
   delete this->_client_pp;
   delete this->_server_pp;
 }
@@ -160,88 +156,55 @@ QUICCrypto::is_handshake_finished() const
 }
 
 int
-QUICCrypto::setup_session()
+QUICCrypto::initialize_key_materials(QUICConnectionId cid)
 {
+  this->_aead              = _get_evp_aead();
+
+  std::unique_ptr<KeyMaterial> km;
+  // for decryption
+  km = this->_keygen_for_client.generate(cid);
+  this->_client_pp->set_key(std::move(km), QUICKeyPhase::CLEARTEXT);
+  // for encryption
+  km = this->_keygen_for_server.generate(cid);
+  this->_server_pp->set_key(std::move(km), QUICKeyPhase::CLEARTEXT);
+  return 1;
+}
+
+int
+QUICCrypto::update_key_materials()
+{
+  QUICKeyPhase next_key_phase;
+  switch (this->_client_pp->key_phase()) {
+  case QUICKeyPhase::PHASE_0:
+    next_key_phase = QUICKeyPhase::PHASE_1;
+    break;
+  case QUICKeyPhase::PHASE_1:
+    next_key_phase = QUICKeyPhase::PHASE_0;
+    break;
+  case QUICKeyPhase::CLEARTEXT:
+    next_key_phase = QUICKeyPhase::PHASE_0;
+    break;
+  }
   const SSL_CIPHER *cipher = SSL_get_current_cipher(this->_ssl);
-  this->_digest            = _get_handshake_digest(cipher);
-  this->_aead              = _get_evp_aead(cipher);
-  this->_hkdf              = new HKDF(this->_digest);
+  this->_aead              = _get_evp_aead();
 
-  size_t secret_len = EVP_MD_size(this->_digest);
+  size_t secret_len = EVP_MD_size(_get_handshake_digest(cipher));
   size_t key_len    = _get_aead_key_len(this->_aead);
   size_t iv_len     = std::max(static_cast<size_t>(8), _get_aead_nonce_len(this->_aead));
 
-  int r = 0;
-
-  r = _export_client_keymaterial(secret_len, key_len, iv_len);
-  if (r != 1) {
-    return r;
-  }
-
-  r = _export_server_keymaterial(secret_len, key_len, iv_len);
-  if (r != 1) {
-    return r;
-  }
+  std::unique_ptr<KeyMaterial> km;
+  // for decryption
+  km = this->_keygen_for_client.generate();
+  this->_client_pp->set_key(std::move(km), next_key_phase);
+  // for encryption
+  km = this->_keygen_for_server.generate();
+  this->_server_pp->set_key(std::move(km), next_key_phase);
 
   Debug(tag, "Negotiated ciper: %s, secret_len: %zu, key_len: %zu, iv_len: %zu", SSL_CIPHER_get_name(cipher), secret_len, key_len,
         iv_len);
   return 1;
 }
 
-/**
- * update client_pp_secret_<N+1> and keying material
- */
-int
-QUICCrypto::update_client_keymaterial()
-{
-  return 0;
-  // KeyMaterial *km_n   = nullptr;
-  // KeyMaterial *km_n_1 = new KeyMaterial(km_n->secret_len, km_n->key_len, km_n->iv_len);
-  // uint8_t secret[256] = {0};
-  // int r               = 0;
-
-  // r = _hkdf_expand_label(secret, km_n->secret_len, km_n->secret, km_n->secret_len, _expand_label_client_1_rtt,
-  //                       sizeof(_expand_label_client_1_rtt), this->_digest);
-  // if (r != 1) {
-  //   return r;
-  // }
-
-  // r = km_n_1->init(this->_aead, this->_digest, secret);
-  // if (r != 1) {
-  //   return r;
-  // }
-  // this->_server_pp->set_key(km_n_1, new_key_phase);
-
-  // return 1;
-}
-
-/**
- * update server_pp_secret_<N+1> and keying material
- */
-int
-QUICCrypto::update_server_keymaterial()
-{
-  return 0;
-  // KeyMaterial *km_n   = nullptr;
-  // KeyMaterial *km_n_1 = new KeyMaterial(km_n->secret_len, km_n->key_len, km_n->iv_len);
-  // uint8_t secret[256] = {0};
-  // int r               = 0;
-
-  // r = _hkdf_expand_label(secret, km_n->secret_len, km_n->secret, km_n->secret_len, _expand_label_server_1_rtt,
-  //                       sizeof(_expand_label_server_1_rtt), this->_digest);
-  // if (r != 1) {
-  //   return r;
-  // }
-
-  // r = km_n_1->init(this->_aead, this->_digest, secret);
-  // if (r != 1) {
-  //   return r;
-  // }
-  // this->_server_pp->set_key(km_n_1, new_key_phase);
-
-  // return 1;
-}
-
 SSL *
 QUICCrypto::ssl_handle()
 {
@@ -252,15 +215,15 @@ bool
 QUICCrypto::encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const uint8_t *plain, size_t plain_len,
                     uint64_t pkt_num, const uint8_t *ad, size_t ad_len, QUICKeyPhase phase) const
 {
-  const KeyMaterial *km = nullptr;
+  QUICPacketProtection *pp = nullptr;
 
   switch (this->_netvc_context) {
   case NET_VCONNECTION_IN: {
-    km = this->_server_pp->get_key(phase);
+    pp = this->_server_pp;
     break;
   }
   case NET_VCONNECTION_OUT: {
-    km = this->_client_pp->get_key(phase);
+    pp = this->_client_pp;
     break;
   }
   default:
@@ -268,24 +231,23 @@ QUICCrypto::encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len,
     return false;
   }
 
-  size_t tag_len = _get_aead_tag_len(SSL_get_current_cipher(this->_ssl));
-  return _encrypt(cipher, cipher_len, max_cipher_len, plain, plain_len, pkt_num, ad, ad_len, km->key, km->key_len, km->iv,
-                  km->iv_len, tag_len);
+  size_t tag_len = this->_get_aead_tag_len();
+  return _encrypt(cipher, cipher_len, max_cipher_len, plain, plain_len, pkt_num, ad, ad_len, pp->get_key(phase), tag_len);
 }
 
 bool
 QUICCrypto::decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, size_t cipher_len,
                     uint64_t pkt_num, const uint8_t *ad, size_t ad_len, QUICKeyPhase phase) const
 {
-  const KeyMaterial *km = nullptr;
+  QUICPacketProtection *pp = nullptr;
 
   switch (this->_netvc_context) {
   case NET_VCONNECTION_IN: {
-    km = this->_client_pp->get_key(phase);
+    pp = this->_client_pp;
     break;
   }
   case NET_VCONNECTION_OUT: {
-    km = this->_server_pp->get_key(phase);
+    pp = this->_server_pp;
     break;
   }
   default:
@@ -293,82 +255,8 @@ QUICCrypto::decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, con
     return false;
   }
 
-  size_t tag_len = _get_aead_tag_len(SSL_get_current_cipher(this->_ssl));
-  return _decrypt(plain, plain_len, max_plain_len, cipher, cipher_len, pkt_num, ad, ad_len, km->key, km->key_len, km->iv,
-                  km->iv_len, tag_len);
-}
-
-int
-QUICCrypto::_export_secret(uint8_t *dst, size_t dst_len, const char *label, size_t label_len) const
-{
-  return SSL_export_keying_material(this->_ssl, dst, dst_len, label, label_len, reinterpret_cast<const uint8_t *>(""), 0, 1);
-}
-
-/**
- * export client_pp_secret_0 and keying material
- */
-int
-QUICCrypto::_export_client_keymaterial(size_t secret_len, size_t key_len, size_t iv_len)
-{
-  KeyMaterial *km = new KeyMaterial(secret_len, key_len, iv_len);
-  int r           = 0;
-
-  r = _export_secret(km->secret, secret_len, exporter_label_client_1_rtt.data(), exporter_label_client_1_rtt.size());
-  if (r != 1) {
-    Debug(tag, "Failed to export secret");
-    return r;
-  }
-
-  r = this->_hkdf->expand_label(km->key, &key_len, km->secret, secret_len, expand_label_key.data(), expand_label_key.size(),
-                                EVP_MD_size(this->_digest));
-  if (r != 1) {
-    Debug(tag, "Failed to expand label for key");
-    return r;
-  }
-
-  r = this->_hkdf->expand_label(km->iv, &iv_len, km->secret, secret_len, expand_label_iv.data(), expand_label_iv.size(),
-                                EVP_MD_size(this->_digest));
-  if (r != 1) {
-    Debug(tag, "Failed to expand label for iv");
-    return r;
-  }
-
-  this->_client_pp->set_key(km, QUICKeyPhase::PHASE_0);
-
-  return 1;
-}
-
-/**
- * export server_pp_secret_0 and keying material
- */
-int
-QUICCrypto::_export_server_keymaterial(size_t secret_len, size_t key_len, size_t iv_len)
-{
-  KeyMaterial *km = new KeyMaterial(secret_len, key_len, iv_len);
-  int r           = 0;
-
-  r = _export_secret(km->secret, secret_len, exporter_label_server_1_rtt.data(), exporter_label_server_1_rtt.size());
-  if (r != 1) {
-    return r;
-  }
-
-  r = this->_hkdf->expand_label(km->key, &key_len, km->secret, secret_len, expand_label_key.data(), expand_label_key.size(),
-                                EVP_MD_size(this->_digest));
-  if (r != 1) {
-    Debug(tag, "Failed to expand label for key");
-    return r;
-  }
-
-  r = this->_hkdf->expand_label(km->iv, &iv_len, km->secret, secret_len, expand_label_iv.data(), expand_label_iv.size(),
-                                EVP_MD_size(this->_digest));
-  if (r != 1) {
-    Debug(tag, "Failed to expand label for iv");
-    return r;
-  }
-
-  this->_server_pp->set_key(km, QUICKeyPhase::PHASE_0);
-
-  return 1;
+  size_t tag_len = this->_get_aead_tag_len();
+  return _decrypt(plain, plain_len, max_plain_len, cipher, cipher_len, pkt_num, ad, ad_len, pp->get_key(phase), tag_len);
 }
 
 /**
diff --git a/iocore/net/quic/QUICCrypto.h b/iocore/net/quic/QUICCrypto.h
index 3e8e0fd..8f5888a 100644
--- a/iocore/net/quic/QUICCrypto.h
+++ b/iocore/net/quic/QUICCrypto.h
@@ -23,7 +23,7 @@
 
 #pragma once
 
-#include "ts/HKDF.h"
+#include "QUICKeyGenerator.h"
 #include <openssl/ssl.h>
 
 #ifdef OPENSSL_IS_BORINGSSL
@@ -37,32 +37,20 @@
 #include "I_NetVConnection.h"
 #include "QUICTypes.h"
 
-struct KeyMaterial {
-  KeyMaterial(size_t secret_len, size_t key_len, size_t iv_len) : secret_len(secret_len), key_len(key_len), iv_len(iv_len) {}
-  uint8_t secret[EVP_MAX_MD_SIZE] = {0};
-  // These constant sizes are not enough somehow
-  // uint8_t key[EVP_MAX_KEY_LENGTH] = {0};
-  // uint8_t iv[EVP_MAX_IV_LENGTH]   = {0};
-  uint8_t key[512]  = {0};
-  uint8_t iv[512]   = {0};
-  size_t secret_len = 0;
-  size_t key_len    = 0;
-  size_t iv_len     = 0;
-};
-
 class QUICPacketProtection
 {
 public:
   QUICPacketProtection(){};
   ~QUICPacketProtection();
-  void set_key(KeyMaterial *km, QUICKeyPhase phase);
-  const KeyMaterial *get_key(QUICKeyPhase phase) const;
+  void set_key(std::unique_ptr<KeyMaterial> km, QUICKeyPhase phase);
+  const KeyMaterial& get_key(QUICKeyPhase phase) const;
   QUICKeyPhase key_phase() const;
 
 private:
-  KeyMaterial *_phase_0_key = nullptr;
-  KeyMaterial *_phase_1_key = nullptr;
-  QUICKeyPhase _key_phase   = QUICKeyPhase::PHASE_UNINITIALIZED;
+  std::unique_ptr<KeyMaterial> _cleartext_key = nullptr;
+  std::unique_ptr<KeyMaterial> _phase_0_key = nullptr;
+  std::unique_ptr<KeyMaterial> _phase_1_key = nullptr;
+  QUICKeyPhase _key_phase   = QUICKeyPhase::CLEARTEXT;
 };
 
 class QUICCrypto
@@ -73,7 +61,8 @@ public:
 
   bool handshake(uint8_t *out, size_t &out_len, size_t max_out_len, const uint8_t *in, size_t in_len);
   bool is_handshake_finished() const;
-  int setup_session();
+  int initialize_key_materials(QUICConnectionId cid);
+  int update_key_materials();
   bool encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const uint8_t *plain, size_t plain_len, uint64_t pkt_num,
                const uint8_t *ad, size_t ad_len, QUICKeyPhase phase) const;
   bool decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, size_t cipher_len, uint64_t pkt_num,
@@ -85,31 +74,25 @@ public:
   SSL *ssl_handle();
 
 private:
-  HKDF *_hkdf = nullptr;
-  int _export_secret(uint8_t *dst, size_t dst_len, const char *label, size_t label_len) const;
-  int _export_client_keymaterial(size_t secret_len, size_t key_len, size_t iv_len);
-  int _export_server_keymaterial(size_t secret_len, size_t key_len, size_t iv_len);
+  QUICKeyGenerator _keygen_for_client = QUICKeyGenerator(QUICKeyGenerator::Context::CLIENT);
+  QUICKeyGenerator _keygen_for_server = QUICKeyGenerator(QUICKeyGenerator::Context::SERVER);
   void _gen_nonce(uint8_t *nonce, size_t &nonce_len, uint64_t pkt_num, const uint8_t *iv, size_t iv_len) const;
-  int _hkdf_expand_label(const uint8_t *dst, size_t dst_len, const uint8_t *secret, size_t secret_len, const char *label,
-                         size_t label_len, const EVP_MD *digest);
 #ifdef OPENSSL_IS_BORINGSSL
-  const EVP_AEAD *_get_evp_aead(const SSL_CIPHER *cipher) const;
+  const EVP_AEAD *_get_evp_aead() const;
   size_t _get_aead_key_len(const EVP_AEAD *aead) const;
   size_t _get_aead_nonce_len(const EVP_AEAD *aead) const;
 #else
-  const EVP_CIPHER *_get_evp_aead(const SSL_CIPHER *cipher) const;
+  const EVP_CIPHER *_get_evp_aead() const;
   size_t _get_aead_key_len(const EVP_CIPHER *aead) const;
   size_t _get_aead_nonce_len(const EVP_CIPHER *aead) const;
 #endif // OPENSSL_IS_BORINGSSL
   const EVP_MD *_get_handshake_digest(const SSL_CIPHER *cipher) const;
-  size_t _get_aead_tag_len(const SSL_CIPHER *cipher) const;
+  size_t _get_aead_tag_len() const;
 
   bool _encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const uint8_t *plain, size_t plain_len,
-                uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, size_t key_len, const uint8_t *iv,
-                size_t iv_len, size_t tag_len) const;
+                uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const KeyMaterial &km, size_t tag_len) const;
   bool _decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, size_t cipher_len, uint64_t pkt_num,
-                const uint8_t *ad, size_t ad_len, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len,
-                size_t tag_len) const;
+                const uint8_t *ad, size_t ad_len, const KeyMaterial &km, size_t tag_len) const;
 
   SSL *_ssl = nullptr;
 #ifdef OPENSSL_IS_BORINGSSL
@@ -117,7 +100,6 @@ private:
 #else
   const EVP_CIPHER *_aead = nullptr;
 #endif // OPENSSL_IS_BORINGSSL
-  const EVP_MD *_digest                  = nullptr;
   QUICPacketProtection *_client_pp       = nullptr;
   QUICPacketProtection *_server_pp       = nullptr;
   NetVConnectionContext_t _netvc_context = NET_VCONNECTION_UNSET;
diff --git a/iocore/net/quic/QUICCrypto_openssl.cc b/iocore/net/quic/QUICCrypto_openssl.cc
index b2858f3..10a9052 100644
--- a/iocore/net/quic/QUICCrypto_openssl.cc
+++ b/iocore/net/quic/QUICCrypto_openssl.cc
@@ -31,21 +31,25 @@
 static constexpr char tag[] = "quic_crypto";
 
 const EVP_CIPHER *
-QUICCrypto::_get_evp_aead(const SSL_CIPHER *cipher) const
+QUICCrypto::_get_evp_aead() const
 {
-  switch (SSL_CIPHER_get_id(cipher)) {
-  case TLS1_3_CK_AES_128_GCM_SHA256:
+  if (this->is_handshake_finished()) {
+    switch (SSL_CIPHER_get_id(SSL_get_current_cipher(this->_ssl))) {
+    case TLS1_3_CK_AES_128_GCM_SHA256:
+      return EVP_aes_128_gcm();
+    case TLS1_3_CK_AES_256_GCM_SHA384:
+      return EVP_aes_256_gcm();
+    case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+      return EVP_chacha20_poly1305();
+    case TLS1_3_CK_AES_128_CCM_SHA256:
+    case TLS1_3_CK_AES_128_CCM_8_SHA256:
+      return EVP_aes_128_ccm();
+    default:
+      ink_assert(false);
+      return nullptr;
+    }
+  } else {
     return EVP_aes_128_gcm();
-  case TLS1_3_CK_AES_256_GCM_SHA384:
-    return EVP_aes_256_gcm();
-  case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
-    return EVP_chacha20_poly1305();
-  case TLS1_3_CK_AES_128_CCM_SHA256:
-  case TLS1_3_CK_AES_128_CCM_8_SHA256:
-    return EVP_aes_128_ccm();
-  default:
-    ink_assert(false);
-    return nullptr;
   }
 }
 
@@ -67,21 +71,25 @@ QUICCrypto::_get_handshake_digest(const SSL_CIPHER *cipher) const
 }
 
 size_t
-QUICCrypto::_get_aead_tag_len(const SSL_CIPHER *cipher) const
+QUICCrypto::_get_aead_tag_len() const
 {
-  switch (SSL_CIPHER_get_id(cipher)) {
-  case TLS1_3_CK_AES_128_GCM_SHA256:
-  case TLS1_3_CK_AES_256_GCM_SHA384:
+  if (this->is_handshake_finished()) {
+    switch (SSL_CIPHER_get_id(SSL_get_current_cipher(this->_ssl))) {
+    case TLS1_3_CK_AES_128_GCM_SHA256:
+    case TLS1_3_CK_AES_256_GCM_SHA384:
+      return EVP_GCM_TLS_TAG_LEN;
+    case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
+      return EVP_CHACHAPOLY_TLS_TAG_LEN;
+    case TLS1_3_CK_AES_128_CCM_SHA256:
+      return EVP_CCM_TLS_TAG_LEN;
+    case TLS1_3_CK_AES_128_CCM_8_SHA256:
+      return EVP_CCM8_TLS_TAG_LEN;
+    default:
+      ink_assert(false);
+      return -1;
+    }
+  } else {
     return EVP_GCM_TLS_TAG_LEN;
-  case TLS1_3_CK_CHACHA20_POLY1305_SHA256:
-    return EVP_CHACHAPOLY_TLS_TAG_LEN;
-  case TLS1_3_CK_AES_128_CCM_SHA256:
-    return EVP_CCM_TLS_TAG_LEN;
-  case TLS1_3_CK_AES_128_CCM_8_SHA256:
-    return EVP_CCM8_TLS_TAG_LEN;
-  default:
-    ink_assert(false);
-    return -1;
   }
 }
 
@@ -99,12 +107,11 @@ QUICCrypto::_get_aead_nonce_len(const EVP_CIPHER *aead) const
 
 bool
 QUICCrypto::_encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len, const uint8_t *plain, size_t plain_len,
-                     uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, size_t key_len, const uint8_t *iv,
-                     size_t iv_len, size_t tag_len) const
+                     uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const KeyMaterial &km, size_t tag_len) const
 {
   uint8_t nonce[EVP_MAX_IV_LENGTH] = {0};
   size_t nonce_len                 = 0;
-  _gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len);
+  _gen_nonce(nonce, nonce_len, pkt_num, km.iv, km.iv_len);
 
   EVP_CIPHER_CTX *aead_ctx;
   int len;
@@ -118,7 +125,7 @@ QUICCrypto::_encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len,
   if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) {
     return false;
   }
-  if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) {
+  if (!EVP_EncryptInit_ex(aead_ctx, nullptr, nullptr, km.key, nonce)) {
     return false;
   }
   if (!EVP_EncryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) {
@@ -149,12 +156,11 @@ QUICCrypto::_encrypt(uint8_t *cipher, size_t &cipher_len, size_t max_cipher_len,
 
 bool
 QUICCrypto::_decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, const uint8_t *cipher, size_t cipher_len,
-                     uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const uint8_t *key, size_t key_len, const uint8_t *iv,
-                     size_t iv_len, size_t tag_len) const
+                     uint64_t pkt_num, const uint8_t *ad, size_t ad_len, const KeyMaterial &km, size_t tag_len) const
 {
   uint8_t nonce[EVP_MAX_IV_LENGTH] = {0};
   size_t nonce_len                 = 0;
-  _gen_nonce(nonce, nonce_len, pkt_num, iv, iv_len);
+  _gen_nonce(nonce, nonce_len, pkt_num, km.iv, km.iv_len);
 
   EVP_CIPHER_CTX *aead_ctx;
   int len;
@@ -168,7 +174,7 @@ QUICCrypto::_decrypt(uint8_t *plain, size_t &plain_len, size_t max_plain_len, co
   if (!EVP_CIPHER_CTX_ctrl(aead_ctx, EVP_CTRL_AEAD_SET_IVLEN, nonce_len, nullptr)) {
     return false;
   }
-  if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, key, nonce)) {
+  if (!EVP_DecryptInit_ex(aead_ctx, nullptr, nullptr, km.key, nonce)) {
     return false;
   }
   if (!EVP_DecryptUpdate(aead_ctx, nullptr, &len, ad, ad_len)) {
diff --git a/iocore/net/quic/QUICHandshake.cc b/iocore/net/quic/QUICHandshake.cc
index b23609f..c33f3b6 100644
--- a/iocore/net/quic/QUICHandshake.cc
+++ b/iocore/net/quic/QUICHandshake.cc
@@ -89,6 +89,7 @@ QUICHandshake::QUICHandshake(QUICConnection *qc, SSL_CTX *ssl_ctx, QUICStateless
   this->_crypto             = new QUICCrypto(this->_ssl, qc->direction());
   this->_version_negotiator = new QUICVersionNegotiator();
 
+  this->_crypto->initialize_key_materials(this->_client_qc->connection_id());
   this->_load_local_transport_parameters();
 
   SET_HANDLER(&QUICHandshake::state_read_client_hello);
@@ -321,12 +322,10 @@ QUICHandshake::_process_client_hello()
   I_WANNA_DUMP_THIS_BUF(msg, msg_len);
   // <----- DEBUG -----
 
-  QUICCrypto *crypto = this->_crypto;
-
   uint8_t server_hello[MAX_HANDSHAKE_MSG_LEN] = {0};
   size_t server_hello_len                     = 0;
   bool result                                 = false;
-  result = crypto->handshake(server_hello, server_hello_len, MAX_HANDSHAKE_MSG_LEN, msg, msg_len);
+  result = this->_crypto->handshake(server_hello, server_hello_len, MAX_HANDSHAKE_MSG_LEN, msg, msg_len);
 
   if (result) {
     // ----- DEBUG ----->
@@ -365,12 +364,10 @@ QUICHandshake::_process_client_finished()
   I_WANNA_DUMP_THIS_BUF(msg, msg_len);
   // <----- DEBUG -----
 
-  QUICCrypto *crypto = this->_crypto;
-
   uint8_t out[MAX_HANDSHAKE_MSG_LEN] = {0};
   size_t out_len                     = 0;
   bool result                        = false;
-  result                             = crypto->handshake(out, out_len, MAX_HANDSHAKE_MSG_LEN, msg, msg_len);
+  result                             = this->_crypto->handshake(out, out_len, MAX_HANDSHAKE_MSG_LEN, msg, msg_len);
 
   if (result) {
     // ----- DEBUG ----->
@@ -398,10 +395,7 @@ QUICHandshake::_process_client_finished()
 QUICErrorUPtr
 QUICHandshake::_process_handshake_complete()
 {
-  QUICCrypto *crypto = this->_crypto;
-  int r              = crypto->setup_session();
-
-  if (r) {
+  if (this->_crypto->update_key_materials()) {
     DebugQHS("Keying Materials are exported");
   } else {
     DebugQHS("Failed to export Keying Materials");
diff --git a/iocore/net/quic/QUICKeyGenerator.cc b/iocore/net/quic/QUICKeyGenerator.cc
new file mode 100644
index 0000000..49277ed
--- /dev/null
+++ b/iocore/net/quic/QUICKeyGenerator.cc
@@ -0,0 +1,105 @@
+/** @file
+ *
+ *  A key generator for QUIC connection
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "QUICKeyGenerator.h"
+#include "ts/HKDF.h"
+
+constexpr static uint8_t QUIC_VERSION_1_SALT[] = {
+  0xaf, 0xc8, 0x24, 0xec, 0x5f, 0xc7, 0x7e, 0xca,
+  0x1e, 0x9d, 0x36, 0xf3, 0x7f, 0xb2, 0xd4, 0x65,
+  0x18, 0xc3, 0x66, 0x39,
+};
+constexpr static ts::string_view LABEL_FOR_CLIENT_CLEARTEXT_SECRET("QUIC client cleartext Secret"_sv);
+constexpr static ts::string_view LABEL_FOR_SERVER_CLEARTEXT_SECRET("QUIC server cleartext Secret"_sv);
+constexpr static ts::string_view LABEL_FOR_KEY("key"_sv);
+constexpr static ts::string_view LABEL_FOR_IV("iv"_sv);
+
+std::unique_ptr<KeyMaterial>
+QUICKeyGenerator::generate(QUICConnectionId cid)
+{
+  std::unique_ptr<KeyMaterial> km = std::make_unique<KeyMaterial>();
+
+  const EVP_MD *md= EVP_sha256();
+  const QUIC_EVP_CIPHER *cipher = this->_get_cipher_for_cleartext();
+  uint8_t cleartext_secret[256];
+  size_t cleartext_secret_len = sizeof(cleartext_secret);
+  uint8_t client_connection_id[8];
+  size_t cid_len = 0;
+  HKDF hkdf(md);
+
+  QUICTypeUtil::write_QUICConnectionId(cid, 8, client_connection_id, &cid_len);
+  if (hkdf.extract(cleartext_secret, &cleartext_secret_len, QUIC_VERSION_1_SALT, sizeof(QUIC_VERSION_1_SALT), client_connection_id, 8) != 1) {
+    return nullptr;
+  }
+
+  switch (this->_ctx) {
+  case Context::CLIENT:
+    this->_generate(
+    km->key, &km->key_len, km->iv, &km->iv_len,
+    hkdf,
+    cleartext_secret, cleartext_secret_len,
+    LABEL_FOR_CLIENT_CLEARTEXT_SECRET.data(), LABEL_FOR_CLIENT_CLEARTEXT_SECRET.length(),
+    md, cipher);
+    break;
+  case Context::SERVER:
+    this->_generate(
+    km->key, &km->key_len, km->iv, &km->iv_len,
+    hkdf,
+    cleartext_secret, cleartext_secret_len,
+    LABEL_FOR_SERVER_CLEARTEXT_SECRET.data(), LABEL_FOR_SERVER_CLEARTEXT_SECRET.length(),
+    md, cipher);
+    break;
+  }
+
+  return km;
+}
+
+std::unique_ptr<KeyMaterial>
+QUICKeyGenerator::generate()
+{
+  return 0;
+}
+
+int
+QUICKeyGenerator::_generate(uint8_t *key, size_t *key_len, uint8_t *iv, size_t *iv_len, HKDF &hkdf, const uint8_t *base_secret, size_t base_secret_len, const char *label, size_t label_len, const EVP_MD *md, const QUIC_EVP_CIPHER *cipher)
+{
+  uint8_t secret[256];
+  size_t secret_len = sizeof(secret);
+    hkdf.expand_label(secret, &secret_len, base_secret, base_secret_len, reinterpret_cast<const char *>(label), label_len, "", 0, EVP_MD_size(md));
+  this->_generate_key(key, key_len, hkdf, secret, secret_len, this->_get_key_len(cipher));
+  this->_generate_iv(iv, iv_len, hkdf, secret, secret_len, this->_get_iv_len(cipher));
+
+  return 0;
+}
+
+int
+QUICKeyGenerator::_generate_key(uint8_t *out, size_t *out_len, HKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t key_length) const
+{
+  return hkdf.expand_label(out, out_len, secret, secret_len, LABEL_FOR_KEY.data(), LABEL_FOR_KEY.length(), "", 0, key_length);
+}
+
+int
+QUICKeyGenerator::_generate_iv(uint8_t *out, size_t *out_len, HKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t iv_length) const
+{
+  return hkdf.expand_label(out, out_len, secret, secret_len, LABEL_FOR_IV.data(), LABEL_FOR_IV.length(), "", 0, iv_length);
+}
diff --git a/iocore/net/quic/QUICKeyGenerator.h b/iocore/net/quic/QUICKeyGenerator.h
new file mode 100644
index 0000000..a8e8c32
--- /dev/null
+++ b/iocore/net/quic/QUICKeyGenerator.h
@@ -0,0 +1,75 @@
+/** @file
+ *
+ *  A key generator for QUIC connection
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+#pragma once
+
+#include <openssl/evp.h>
+#include "QUICTypes.h"
+#include "ts/HKDF.h"
+
+#ifdef OPENSSL_IS_BORINGSSL
+typedef EVP_AEAD QUIC_EVP_CIPHER;
+#else
+typedef EVP_CIPHER QUIC_EVP_CIPHER;
+#endif // OPENSSL_IS_BORINGSSL
+
+struct KeyMaterial {
+  // These constant sizes are not enough somehow
+  // uint8_t key[EVP_MAX_KEY_LENGTH] = {0};
+  // uint8_t iv[EVP_MAX_IV_LENGTH]   = {0};
+  uint8_t key[512]  = {0};
+  uint8_t iv[512]   = {0};
+  size_t key_len    = 512;
+  size_t iv_len     = 512;
+};
+
+class QUICKeyGenerator
+{
+public:
+  enum class Context {
+    SERVER, CLIENT
+  };
+
+  QUICKeyGenerator(Context ctx) : _ctx(ctx) {}
+
+  /*
+   * Gnerate a key and an IV for Cleartext
+   */
+  std::unique_ptr<KeyMaterial> generate(QUICConnectionId cid);
+
+  /*
+   * Generate a key and an IV for Packet Protection
+   * 
+   * On the first call, this generates a secret with the constatnt label and generate a key material with the secret.
+   * On the following call, this regenerates a new secret based on the last secret and genereate a new key material with the new secret.
+   */
+  std::unique_ptr<KeyMaterial> generate();
+
+private:
+  Context _ctx = Context::SERVER;
+  int _generate(uint8_t *key, size_t *key_len, uint8_t *iv, size_t *iv_len, HKDF &hkdf, const uint8_t *secret, size_t secret_len, const char *label, size_t label_len, const EVP_MD *md, const QUIC_EVP_CIPHER *cipher);
+  int _generate_key(uint8_t *out, size_t *out_len, HKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t key_length) const;
+  int _generate_iv(uint8_t *out, size_t *out_len, HKDF &hkdf, const uint8_t *secret, size_t secret_len, size_t iv_length) const;
+  size_t _get_key_len(const QUIC_EVP_CIPHER *cipher) const;
+  size_t _get_iv_len(const QUIC_EVP_CIPHER *cipher) const;
+  const QUIC_EVP_CIPHER *_get_cipher_for_cleartext() const;
+};
diff --git a/lib/ts/HKDF.cc b/iocore/net/quic/QUICKeyGenerator_openssl.cc
similarity index 54%
copy from lib/ts/HKDF.cc
copy to iocore/net/quic/QUICKeyGenerator_openssl.cc
index fe01117..a7fb4ac 100644
--- a/lib/ts/HKDF.cc
+++ b/iocore/net/quic/QUICKeyGenerator_openssl.cc
@@ -1,6 +1,6 @@
 /** @file
  *
- *  HKDF utility (common part)
+ *  A key generator for QUIC connection (OpenSSL specific parts)
  *
  *  @section license License
  *
@@ -20,25 +20,22 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-#include "HKDF.h"
-#include <cstdio>
-#include <cstring>
+#include "QUICKeyGenerator.h"
 
-int
-HKDF::expand_label(uint8_t *dst, size_t *dst_len, const uint8_t *secret, size_t secret_len, const char *label, size_t label_len,
-                   uint16_t length)
+size_t
+QUICKeyGenerator::_get_key_len(const QUIC_EVP_CIPHER *cipher) const
 {
-  // Create HKDF label
-  uint8_t hkdf_label[512]; // 2 + 255 + 255
-  // Length
-  hkdf_label[0] = (length >> 8) & 0xFF;
-  hkdf_label[1] = length & 0xFF;
-  // "tls13 " + Label
-  int hkdf_label_len = sprintf(reinterpret_cast<char *>(hkdf_label + 2), "tls13 %.*s", static_cast<int>(label_len), label);
-  // Always 0
-  hkdf_label[hkdf_label_len] = 0;
-  ++hkdf_label_len;
+  return EVP_CIPHER_key_length(cipher);
+}
 
-  this->expand(dst, dst_len, secret, secret_len, hkdf_label, hkdf_label_len, length);
-  return 1;
+size_t
+QUICKeyGenerator::_get_iv_len(const QUIC_EVP_CIPHER *cipher) const
+{
+  return EVP_CIPHER_iv_length(cipher);
+}
+
+const QUIC_EVP_CIPHER *
+QUICKeyGenerator::_get_cipher_for_cleartext() const
+{
+  return EVP_aes_128_gcm();
 }
diff --git a/iocore/net/quic/QUICPacket.cc b/iocore/net/quic/QUICPacket.cc
index 95572cd..82e0b74 100644
--- a/iocore/net/quic/QUICPacket.cc
+++ b/iocore/net/quic/QUICPacket.cc
@@ -174,6 +174,16 @@ QUICPacketLongHeader::payload() const
   }
 }
 
+uint16_t
+QUICPacketHeader::payload_size() const
+{
+  if (this->_buf) {
+    return this->_buf_len - this->length();
+  } else {
+    return this->_payload_len;
+  }
+}
+
 bool
 QUICPacketLongHeader::has_key_phase() const
 {
@@ -233,7 +243,6 @@ QUICPacketShortHeader::QUICPacketShortHeader(QUICPacketType type, QUICPacketNumb
     this->_key_phase = QUICKeyPhase::PHASE_1;
   } else {
     ink_assert(false);
-    this->_key_phase = QUICKeyPhase::PHASE_UNINITIALIZED;
   }
 }
 
@@ -254,11 +263,8 @@ QUICPacketShortHeader::QUICPacketShortHeader(QUICPacketType type, QUICConnection
     this->_key_phase = QUICKeyPhase::PHASE_0;
   } else if (type == QUICPacketType::ONE_RTT_PROTECTED_KEY_PHASE_1) {
     this->_key_phase = QUICKeyPhase::PHASE_1;
-  } else if (type == QUICPacketType::STATELESS_RESET) {
-    this->_key_phase = QUICKeyPhase::PHASE_UNINITIALIZED;
   } else {
     ink_assert(false);
-    this->_key_phase = QUICKeyPhase::PHASE_UNINITIALIZED;
   }
 }
 
@@ -445,10 +451,12 @@ QUICPacketShortHeader::store(uint8_t *buf, size_t *len) const
 //
 // QUICPacket
 //
-QUICPacket::QUICPacket(ats_unique_buf buf, size_t len, QUICPacketNumber base_packet_number)
+QUICPacket::QUICPacket(QUICPacketHeader *header, ats_unique_buf unprotected_payload, size_t unprotected_payload_len, QUICPacketNumber base_packet_number)
 {
-  this->_size   = len;
-  this->_header = QUICPacketHeader::load(reinterpret_cast<const uint8_t *>(buf.release()), len, base_packet_number);
+  this->_size   = unprotected_payload_len;
+  this->_header = header;
+  this->_unprotected_payload     = std::move(unprotected_payload);
+  this->_unprotected_payload_len = unprotected_payload_len;
 }
 
 QUICPacket::QUICPacket(QUICPacketType type, QUICConnectionId connection_id, QUICPacketNumber packet_number,
@@ -577,7 +585,6 @@ QUICPacket::header_size() const
 uint16_t
 QUICPacket::payload_size() const
 {
-  // FIXME Protected packets may / may not contain something at the end
   if (this->type() == QUICPacketType::STATELESS_RESET) {
     return this->_size - this->_header->length();
   } else if (this->type() != QUICPacketType::ZERO_RTT_PROTECTED && this->type() != QUICPacketType::ONE_RTT_PROTECTED_KEY_PHASE_0 &&
@@ -608,8 +615,8 @@ QUICPacket::store(uint8_t *buf, size_t *len) const
     memcpy(buf + *len, this->payload(), this->payload_size());
     *len += this->payload_size();
 
-    fnv1a(buf, *len, buf + *len);
-    *len += FNV1A_HASH_LEN;
+    // fnv1a(buf, *len, buf + *len);
+    // *len += FNV1A_HASH_LEN;
   } else {
     ink_assert(this->_protected_payload);
     memcpy(buf + *len, this->_protected_payload.get(), this->_protected_payload_size);
@@ -623,14 +630,6 @@ QUICPacket::store_header(uint8_t *buf, size_t *len) const
   this->_header->store(buf, len);
 }
 
-bool
-QUICPacket::has_valid_fnv1a_hash() const
-{
-  uint8_t hash[FNV1A_HASH_LEN];
-  fnv1a(reinterpret_cast<const uint8_t *>(this->_header->buf()), this->size() - FNV1A_HASH_LEN, hash);
-  return memcmp(this->_header->buf() + this->size() - FNV1A_HASH_LEN, hash, 8) == 0;
-}
-
 void
 QUICPacket::set_protected_payload(ats_unique_buf cipher_txt, size_t cipher_txt_len)
 {
@@ -691,10 +690,55 @@ QUICPacket::decode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, si
 // QUICPacketFactory
 //
 QUICPacketUPtr
-QUICPacketFactory::create(ats_unique_buf buf, size_t len, QUICPacketNumber base_packet_number)
-{
-  QUICPacket *packet = quicPacketAllocator.alloc();
-  new (packet) QUICPacket(std::move(buf), len, base_packet_number);
+QUICPacketFactory::create(ats_unique_buf buf, size_t len, QUICPacketNumber base_packet_number, QUICPacketCreationResult &result)
+{
+  size_t max_plain_txt_len = 2048;
+  ats_unique_buf plain_txt = ats_unique_malloc(max_plain_txt_len);
+  size_t plain_txt_len     = 0;
+
+  QUICPacketHeader *header = QUICPacketHeader::load(buf.release(), len, base_packet_number);
+
+  switch (header->type()) {
+  case QUICPacketType::VERSION_NEGOTIATION:
+  case QUICPacketType::STATELESS_RESET:
+    // These packets are unprotected. Just copy the payload
+    memcpy(plain_txt.get(), header->payload(), header->payload_size());
+    plain_txt_len = header->payload_size();
+    result = QUICPacketCreationResult::SUCCESS;
+    break;
+  case QUICPacketType::ONE_RTT_PROTECTED_KEY_PHASE_0:
+  case QUICPacketType::ONE_RTT_PROTECTED_KEY_PHASE_1:
+    if (this->_crypto->is_handshake_finished()) {
+      if (this->_crypto->decrypt(plain_txt.get(), plain_txt_len, max_plain_txt_len, header->payload(), header->payload_size(),
+                                 header->packet_number(), header->buf(), header->length(), header->key_phase())) {
+        result = QUICPacketCreationResult::SUCCESS;
+      } else {
+        result = QUICPacketCreationResult::FAILED;
+      }
+    } else {
+      result = QUICPacketCreationResult::NOT_READY;
+    }
+    break;
+  case QUICPacketType::CLIENT_INITIAL:
+  case QUICPacketType::CLIENT_CLEARTEXT:
+  case QUICPacketType::SERVER_CLEARTEXT:
+      if (this->_crypto->decrypt(plain_txt.get(), plain_txt_len, max_plain_txt_len, header->payload(), header->payload_size(),
+                                 header->packet_number(), header->buf(), header->length(), QUICKeyPhase::CLEARTEXT)) {
+        result = QUICPacketCreationResult::SUCCESS;
+      } else {
+        result = QUICPacketCreationResult::FAILED;
+      }
+  default:
+    result = QUICPacketCreationResult::FAILED;
+    break;
+  }
+
+  QUICPacket *packet = nullptr;
+  if (result == QUICPacketCreationResult::SUCCESS) {
+    packet = quicPacketAllocator.alloc();
+    new (packet) QUICPacket(header, std::move(plain_txt), plain_txt_len, base_packet_number);
+  }
+
   return QUICPacketUPtr(packet, &QUICPacketDeleter::delete_packet);
 }
 
diff --git a/iocore/net/quic/QUICPacket.h b/iocore/net/quic/QUICPacket.h
index 987b90e..1874447 100644
--- a/iocore/net/quic/QUICPacket.h
+++ b/iocore/net/quic/QUICPacket.h
@@ -39,13 +39,14 @@
 class QUICPacketHeader
 {
 public:
-  QUICPacketHeader(const uint8_t *buf, size_t len, QUICPacketNumber base) : _buf(buf), _base_packet_number(base) {}
+  QUICPacketHeader(const uint8_t *buf, size_t len, QUICPacketNumber base) : _buf(buf), _buf_len(len), _base_packet_number(base) {}
   const uint8_t *buf();
   virtual QUICPacketType type() const            = 0;
   virtual QUICConnectionId connection_id() const = 0;
   virtual QUICPacketNumber packet_number() const = 0;
   virtual QUICVersion version() const            = 0;
   virtual const uint8_t *payload() const         = 0;
+  uint16_t payload_size() const;
   virtual QUICKeyPhase key_phase() const         = 0;
   virtual uint16_t length() const                = 0;
   virtual void store(uint8_t *buf, size_t *len) const = 0;
@@ -65,9 +66,10 @@ protected:
   QUICPacketHeader(){};
 
   const uint8_t *_buf                  = nullptr;
+  size_t _buf_len = 0;
   ats_unique_buf _payload              = ats_unique_buf(nullptr, [](void *p) { ats_free(p); });
   QUICPacketType _type                 = QUICPacketType::UNINITIALIZED;
-  QUICKeyPhase _key_phase              = QUICKeyPhase::PHASE_UNINITIALIZED;
+  QUICKeyPhase _key_phase              = QUICKeyPhase::CLEARTEXT;
   QUICConnectionId _connection_id      = 0;
   QUICPacketNumber _packet_number      = 0;
   QUICPacketNumber _base_packet_number = 0;
@@ -129,7 +131,7 @@ class QUICPacket
 {
 public:
   QUICPacket(){};
-  QUICPacket(ats_unique_buf buf, size_t len, QUICPacketNumber base_packet_number);
+  QUICPacket(QUICPacketHeader *header, ats_unique_buf unprotected_payload, size_t unprotected_payload_len, QUICPacketNumber base_packet_number);
   QUICPacket(QUICPacketType type, QUICConnectionId connection_id, QUICPacketNumber packet_number,
              QUICPacketNumber base_packet_number, QUICVersion version, ats_unique_buf payload, size_t len, bool retransmittable);
   QUICPacket(QUICPacketType type, QUICPacketNumber packet_number, QUICPacketNumber base_packet_number, ats_unique_buf payload,
@@ -152,7 +154,6 @@ public:
   uint16_t payload_size() const;
   void store(uint8_t *buf, size_t *len) const;
   void store_header(uint8_t *buf, size_t *len) const;
-  bool has_valid_fnv1a_hash() const;
   QUICKeyPhase key_phase() const;
   static uint8_t calc_packet_number_len(QUICPacketNumber num, QUICPacketNumber base);
   static bool encode_packet_number(QUICPacketNumber &dst, QUICPacketNumber src, size_t len);
@@ -164,6 +165,8 @@ private:
   ats_unique_buf _protected_payload = ats_unique_buf(nullptr, [](void *p) { ats_free(p); });
   size_t _size                      = 0;
   size_t _protected_payload_size    = 0;
+  ats_unique_buf _unprotected_payload = ats_unique_buf(nullptr, [](void *p) { ats_free(p); });
+  size_t _unprotected_payload_len = 0;
   QUICPacketHeader *_header;
   bool _is_retransmittable = false;
 };
@@ -205,7 +208,7 @@ public:
 class QUICPacketFactory
 {
 public:
-  static QUICPacketUPtr create(ats_unique_buf buf, size_t len, QUICPacketNumber base_packet_number);
+  QUICPacketUPtr create(ats_unique_buf buf, size_t len, QUICPacketNumber base_packet_number, QUICPacketCreationResult &result);
   QUICPacketUPtr create_version_negotiation_packet(const QUICPacket *packet_sent_by_client, QUICPacketNumber base_packet_number);
   QUICPacketUPtr create_server_cleartext_packet(QUICConnectionId connection_id, QUICPacketNumber base_packet_number,
                                                 ats_unique_buf payload, size_t len, bool retransmittable);
@@ -222,5 +225,3 @@ private:
   QUICCrypto *_crypto  = nullptr;
   QUICPacketNumberGenerator _packet_number_generator;
 };
-
-void fnv1a(const uint8_t *data, size_t len, uint8_t *hash);
diff --git a/iocore/net/quic/QUICTypes.cc b/iocore/net/quic/QUICTypes.cc
index 7427d6a..27fa72c 100644
--- a/iocore/net/quic/QUICTypes.cc
+++ b/iocore/net/quic/QUICTypes.cc
@@ -147,20 +147,6 @@ QUICTypeUtil::write_uint_as_nbytes(uint64_t value, uint8_t n, uint8_t *buf, size
   *len = n;
 }
 
-void
-fnv1a(const uint8_t *data, size_t len, uint8_t *hash)
-{
-  uint64_t h     = 0xcbf29ce484222325ULL;
-  uint64_t prime = 0x100000001b3ULL;
-  size_t n;
-
-  for (size_t i = 0; i < len; ++i) {
-    h ^= data[i];
-    h *= prime;
-  }
-  return QUICTypeUtil::write_uint_as_nbytes(h, 8, hash, &n);
-}
-
 uint16_t
 QUICError::code()
 {
diff --git a/iocore/net/quic/QUICTypes.h b/iocore/net/quic/QUICTypes.h
index 2e78939..ee8dbbe 100644
--- a/iocore/net/quic/QUICTypes.h
+++ b/iocore/net/quic/QUICTypes.h
@@ -113,7 +113,13 @@ enum class QUICVersionNegotiationStatus {
 enum class QUICKeyPhase : int {
   PHASE_0 = 0,
   PHASE_1,
-  PHASE_UNINITIALIZED,
+  CLEARTEXT,
+};
+
+enum class QUICPacketCreationResult {
+  SUCCESS,
+  FAILED,
+  NOT_READY,
 };
 
 enum class QUICErrorClass {
diff --git a/iocore/net/quic/test/Makefile.am b/iocore/net/quic/test/Makefile.am
index 5b75729..f43d35d 100644
--- a/iocore/net/quic/test/Makefile.am
+++ b/iocore/net/quic/test/Makefile.am
@@ -26,6 +26,7 @@ check_PROGRAMS = \
   test_QUICStream \
   test_QUICStreamManager \
   test_QUICTransportParameters \
+  test_QUICKeyGenerator \
   test_QUICCrypto \
   test_QUICLossDetector \
   test_QUICTypeUtil \
@@ -50,8 +51,10 @@ AM_CPPFLAGS += \
   @OPENSSL_INCLUDES@
 
 if OPENSSL_IS_BORINGSSL
+QUICKeyGenerator_impl = ../QUICKeyGenerator_boringssl.cc
 QUICCrypto_impl = ../QUICCrypto_boringssl.cc
 else
+QUICKeyGenerator_impl = ../QUICKeyGenerator_openssl.cc
 QUICCrypto_impl = ../QUICCrypto_openssl.cc
 endif
 
@@ -317,6 +320,29 @@ test_QUICTransportParameters_LDADD = \
   $(top_builddir)/iocore/eventsystem/libinkevent.a
 
 #
+# test_QUICKeyGenerator
+#
+test_QUICKeyGenerator_CPPFLAGS = \
+  $(AM_CPPFLAGS)
+
+test_QUICKeyGenerator_LDFLAGS = \
+  @AM_LDFLAGS@ \
+  @OPENSSL_LDFLAGS@
+
+test_QUICKeyGenerator_LDADD = \
+  @OPENSSL_LIBS@ \
+  $(top_builddir)/lib/ts/libtsutil.la \
+  $(top_builddir)/proxy/shared/libUglyLogStubs.a \
+  $(top_builddir)/iocore/eventsystem/libinkevent.a
+
+test_QUICKeyGenerator_SOURCES = \
+  main.cc \
+  test_QUICKeyGenerator.cc \
+  ../QUICKeyGenerator.cc \
+  $(QUICKeyGenerator_impl) \
+  ../QUICTypes.cc
+
+#
 # test_QUICCrypto
 #
 test_QUICCrypto_CPPFLAGS = \
diff --git a/iocore/net/quic/test/test_QUICKeyGenerator.cc b/iocore/net/quic/test/test_QUICKeyGenerator.cc
new file mode 100644
index 0000000..cdae44a2
--- /dev/null
+++ b/iocore/net/quic/test/test_QUICKeyGenerator.cc
@@ -0,0 +1,107 @@
+/** @file
+ *
+ *  A brief file description
+ *
+ *  @section license License
+ *
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+#include "catch.hpp"
+
+#include <cstring>
+
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/base.h>
+#endif
+
+#include <openssl/ssl.h>
+
+#include "Mock.h"
+#include "QUICKeyGenerator.h"
+
+void
+print_hex(const uint8_t *v, size_t len)
+{
+  for (size_t i = 0; i < len; i++) {
+    std::cout << std::setw(2) << std::setfill('0') << std::hex << static_cast<uint32_t>(v[i]) << " ";
+
+    if (i != 0 && (i + 1) % 32 == 0 && i != len - 1) {
+      std::cout << std::endl;
+    }
+  }
+
+  std::cout << std::endl;
+
+  return;
+}
+
+TEST_CASE("QUICKeyGenerator", "[quic]")
+{
+
+  SECTION("CLIENT Cleartext")
+  {
+    QUICKeyGenerator keygen(QUICKeyGenerator::Context::CLIENT);
+
+    QUICConnectionId cid = 0x8394c8f03e515708;
+    uint8_t expected_client_key[] = {
+      0x2e, 0xbd, 0x78, 0x00, 0xdb, 0xed, 0x20, 0x10,
+      0xe5, 0xa2, 0x1c, 0x4a, 0xd2, 0x4b, 0x4e, 0xc3
+    };
+    uint8_t expected_client_iv[] = {
+      0x55, 0x44, 0x0d, 0x5f, 0xf7, 0x50, 0x3d, 0xe4,
+      0x99, 0x7b, 0xfd, 0x6b
+    };
+    uint8_t actual_client_key[128];
+    size_t actual_client_key_len = sizeof(actual_client_key);
+    uint8_t actual_client_iv[128];
+    size_t actual_client_iv_len = sizeof(actual_client_iv);
+
+    keygen.generate(cid, actual_client_key, &actual_client_key_len, actual_client_iv, &actual_client_iv_len);
+
+    CHECK(actual_client_key_len == sizeof(expected_client_key));
+    CHECK(memcmp(actual_client_key, expected_client_key, sizeof(expected_client_key)) == 0);
+    CHECK(actual_client_iv_len == sizeof(expected_client_iv));
+    CHECK(memcmp(actual_client_iv, expected_client_iv, sizeof(expected_client_iv)) == 0);
+  }
+
+  SECTION("SERVER Cleartext")
+  {
+    QUICKeyGenerator keygen(QUICKeyGenerator::Context::SERVER);
+
+    QUICConnectionId cid = 0x8394c8f03e515708;
+    uint8_t expected_server_key[] = {
+      0xc8, 0xea, 0x1b, 0xc1, 0x71, 0xe5, 0x2b, 0xae,
+      0x71, 0xfb, 0x78, 0x39, 0x52, 0xc7, 0xb8, 0xfc
+    };
+    uint8_t expected_server_iv[] = {
+      0x57, 0x82, 0x3b, 0x85, 0x2c, 0x7e, 0xf9, 0xe3,
+      0x80, 0x2b, 0x69, 0x0b
+    };
+    uint8_t actual_server_key[128];
+    size_t actual_server_key_len = sizeof(actual_server_key);
+    uint8_t actual_server_iv[128];
+    size_t actual_server_iv_len = sizeof(actual_server_iv);
+
+    keygen.generate(cid, actual_server_key, &actual_server_key_len, actual_server_iv, &actual_server_iv_len);
+
+    CHECK(actual_server_key_len == sizeof(expected_server_key));
+    CHECK(memcmp(actual_server_key, expected_server_key, sizeof(expected_server_key)) == 0);
+    CHECK(actual_server_iv_len == sizeof(expected_server_iv));
+    CHECK(memcmp(actual_server_iv, expected_server_iv, sizeof(expected_server_iv)) == 0);
+  }
+}
diff --git a/lib/ts/HKDF.cc b/lib/ts/HKDF.cc
index fe01117..6da55c4 100644
--- a/lib/ts/HKDF.cc
+++ b/lib/ts/HKDF.cc
@@ -26,18 +26,19 @@
 
 int
 HKDF::expand_label(uint8_t *dst, size_t *dst_len, const uint8_t *secret, size_t secret_len, const char *label, size_t label_len,
-                   uint16_t length)
+                   const char *hash_value, size_t hash_value_len, uint16_t length)
 {
   // Create HKDF label
   uint8_t hkdf_label[512]; // 2 + 255 + 255
+  int hkdf_label_len = 0;
   // Length
   hkdf_label[0] = (length >> 8) & 0xFF;
   hkdf_label[1] = length & 0xFF;
+  hkdf_label_len += 2;
   // "tls13 " + Label
-  int hkdf_label_len = sprintf(reinterpret_cast<char *>(hkdf_label + 2), "tls13 %.*s", static_cast<int>(label_len), label);
-  // Always 0
-  hkdf_label[hkdf_label_len] = 0;
-  ++hkdf_label_len;
+  hkdf_label_len += sprintf(reinterpret_cast<char *>(hkdf_label + hkdf_label_len), "%ctls13 %.*s", static_cast<int>(6 + label_len), static_cast<int>(label_len), label);
+  // Hash Value
+  hkdf_label_len += sprintf(reinterpret_cast<char *>(hkdf_label + hkdf_label_len), "%c%.*s", static_cast<int>(hash_value_len), static_cast<int>(hash_value_len), hash_value);
 
   this->expand(dst, dst_len, secret, secret_len, hkdf_label, hkdf_label_len, length);
   return 1;
diff --git a/lib/ts/HKDF.h b/lib/ts/HKDF.h
index e659ada..2905299 100644
--- a/lib/ts/HKDF.h
+++ b/lib/ts/HKDF.h
@@ -40,7 +40,7 @@ public:
 
   // This function is technically a part of TLS 1.3
   int expand_label(uint8_t *dst, size_t *dst_len, const uint8_t *secret, size_t secret_len, const char *label, size_t label_len,
-                   uint16_t length);
+                   const char *hash_value, size_t hash_value_len, uint16_t length);
 
 private:
   const EVP_MD *_digest = nullptr;
diff --git a/lib/ts/HKDF_openssl.cc b/lib/ts/HKDF_openssl.cc
index af0e87f..d8ff3e4 100644
--- a/lib/ts/HKDF_openssl.cc
+++ b/lib/ts/HKDF_openssl.cc
@@ -25,12 +25,16 @@
 
 HKDF::HKDF(const EVP_MD *digest) : _digest(digest)
 {
-  this->_pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
+  // XXX We cannot reuse pctx now due to a bug in OpenSSL
+  // this->_pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
 }
 
 int
 HKDF::extract(uint8_t *dst, size_t *dst_len, const uint8_t *salt, size_t salt_len, const uint8_t *ikm, size_t ikm_len)
 {
+  // XXX See comments in the constructor
+  this->_pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
+
   if (EVP_PKEY_derive_init(this->_pctx) != 1) {
     return -1;
   }
@@ -49,13 +53,23 @@ HKDF::extract(uint8_t *dst, size_t *dst_len, const uint8_t *salt, size_t salt_le
   if (EVP_PKEY_derive(this->_pctx, dst, dst_len) != 1) {
     return -6;
   }
+
+  /// XXX See comments in constuctor.
+  EVP_PKEY_CTX_free(this->_pctx);
+
   return 1;
+
+  // XXX See comments in the constructor
+  EVP_PKEY_CTX_free(this->_pctx);
 }
 
 int
 HKDF::expand(uint8_t *dst, size_t *dst_len, const uint8_t *prk, size_t prk_len, const uint8_t *info, size_t info_len,
              uint16_t length)
 {
+  // XXX See comments in the constructor
+  this->_pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_HKDF, nullptr);
+
   if (EVP_PKEY_derive_init(this->_pctx) != 1) {
     return -1;
   }
@@ -76,5 +90,8 @@ HKDF::expand(uint8_t *dst, size_t *dst_len, const uint8_t *prk, size_t prk_len,
     return -7;
   }
 
+  // XXX See comments in the constructor
+  EVP_PKEY_CTX_free(this->_pctx);
+
   return 1;
 }

-- 
To stop receiving notification emails like this one, please contact
['"commits@trafficserver.apache.org" <co...@trafficserver.apache.org>'].