Add support for Header Protection to QuicCrypter classes

gfe-relnote: no behavior change (adds unused methods to QuicCrypter classes)
PiperOrigin-RevId: 239293044
Change-Id: I0365ad92962102714787b44d346f5c698900ead9
diff --git a/quic/core/crypto/aes_128_gcm_12_decrypter.cc b/quic/core/crypto/aes_128_gcm_12_decrypter.cc
index f76246b..3442807 100644
--- a/quic/core/crypto/aes_128_gcm_12_decrypter.cc
+++ b/quic/core/crypto/aes_128_gcm_12_decrypter.cc
@@ -17,11 +17,11 @@
 }  // namespace
 
 Aes128Gcm12Decrypter::Aes128Gcm12Decrypter()
-    : AeadBaseDecrypter(EVP_aead_aes_128_gcm,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ false) {
+    : AesBaseDecrypter(EVP_aead_aes_128_gcm,
+                       kKeySize,
+                       kAuthTagSize,
+                       kNonceSize,
+                       /* use_ietf_nonce_construction */ false) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/aes_128_gcm_12_decrypter.h b/quic/core/crypto/aes_128_gcm_12_decrypter.h
index 7844f00..d5686e8 100644
--- a/quic/core/crypto/aes_128_gcm_12_decrypter.h
+++ b/quic/core/crypto/aes_128_gcm_12_decrypter.h
@@ -7,7 +7,7 @@
 
 #include <cstdint>
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -18,7 +18,7 @@
 //
 // It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
 // of the nonce is four bytes.
-class QUIC_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AeadBaseDecrypter {
+class QUIC_EXPORT_PRIVATE Aes128Gcm12Decrypter : public AesBaseDecrypter {
  public:
   enum {
     // Authentication tags are truncated to 96 bits.
diff --git a/quic/core/crypto/aes_128_gcm_12_encrypter.cc b/quic/core/crypto/aes_128_gcm_12_encrypter.cc
index d488463..d48d5a5 100644
--- a/quic/core/crypto/aes_128_gcm_12_encrypter.cc
+++ b/quic/core/crypto/aes_128_gcm_12_encrypter.cc
@@ -16,11 +16,11 @@
 }  // namespace
 
 Aes128Gcm12Encrypter::Aes128Gcm12Encrypter()
-    : AeadBaseEncrypter(EVP_aead_aes_128_gcm,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ false) {
+    : AesBaseEncrypter(EVP_aead_aes_128_gcm,
+                       kKeySize,
+                       kAuthTagSize,
+                       kNonceSize,
+                       /* use_ietf_nonce_construction */ false) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/aes_128_gcm_12_encrypter.h b/quic/core/crypto/aes_128_gcm_12_encrypter.h
index 5eb4e8e..cd80fbf 100644
--- a/quic/core/crypto/aes_128_gcm_12_encrypter.h
+++ b/quic/core/crypto/aes_128_gcm_12_encrypter.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
 #define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_12_ENCRYPTER_H_
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -16,7 +16,7 @@
 //
 // It uses an authentication tag of 12 bytes (96 bits). The fixed prefix
 // of the nonce is four bytes.
-class QUIC_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AeadBaseEncrypter {
+class QUIC_EXPORT_PRIVATE Aes128Gcm12Encrypter : public AesBaseEncrypter {
  public:
   enum {
     // Authentication tags are truncated to 96 bits.
diff --git a/quic/core/crypto/aes_128_gcm_decrypter.cc b/quic/core/crypto/aes_128_gcm_decrypter.cc
index 43616ad..650b245 100644
--- a/quic/core/crypto/aes_128_gcm_decrypter.cc
+++ b/quic/core/crypto/aes_128_gcm_decrypter.cc
@@ -19,11 +19,11 @@
 }  // namespace
 
 Aes128GcmDecrypter::Aes128GcmDecrypter()
-    : AeadBaseDecrypter(EVP_aead_aes_128_gcm,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ true) {
+    : AesBaseDecrypter(EVP_aead_aes_128_gcm,
+                       kKeySize,
+                       kAuthTagSize,
+                       kNonceSize,
+                       /* use_ietf_nonce_construction */ true) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/aes_128_gcm_decrypter.h b/quic/core/crypto/aes_128_gcm_decrypter.h
index 4b90d83..6ddbe01 100644
--- a/quic/core/crypto/aes_128_gcm_decrypter.h
+++ b/quic/core/crypto/aes_128_gcm_decrypter.h
@@ -7,7 +7,7 @@
 
 #include <cstdint>
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -17,7 +17,7 @@
 //
 // It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
 // that is XOR'd with the packet number to compute the nonce.
-class QUIC_EXPORT_PRIVATE Aes128GcmDecrypter : public AeadBaseDecrypter {
+class QUIC_EXPORT_PRIVATE Aes128GcmDecrypter : public AesBaseDecrypter {
  public:
   enum {
     kAuthTagSize = 16,
diff --git a/quic/core/crypto/aes_128_gcm_decrypter_test.cc b/quic/core/crypto/aes_128_gcm_decrypter_test.cc
index 9e3d84d..13c6827 100644
--- a/quic/core/crypto/aes_128_gcm_decrypter_test.cc
+++ b/quic/core/crypto/aes_128_gcm_decrypter_test.cc
@@ -271,5 +271,21 @@
   }
 }
 
+TEST_F(Aes128GcmDecrypterTest, GenerateHeaderProtectionMask) {
+  Aes128GcmDecrypter decrypter;
+  std::string key =
+      QuicTextUtils::HexDecode("d9132370cb18476ab833649cf080d970");
+  std::string sample =
+      QuicTextUtils::HexDecode("d1d7998068517adb769b48b924a32c47");
+  QuicDataReader sample_reader(sample.data(), sample.size());
+  ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+  std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+  std::string expected_mask =
+      QuicTextUtils::HexDecode("b132c37d6164da4ea4dc9b763aceec27");
+  test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+                                      mask.size(), expected_mask.data(),
+                                      expected_mask.size());
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/crypto/aes_128_gcm_encrypter.cc b/quic/core/crypto/aes_128_gcm_encrypter.cc
index ed08a41..7126578 100644
--- a/quic/core/crypto/aes_128_gcm_encrypter.cc
+++ b/quic/core/crypto/aes_128_gcm_encrypter.cc
@@ -16,11 +16,11 @@
 }  // namespace
 
 Aes128GcmEncrypter::Aes128GcmEncrypter()
-    : AeadBaseEncrypter(EVP_aead_aes_128_gcm,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ true) {
+    : AesBaseEncrypter(EVP_aead_aes_128_gcm,
+                       kKeySize,
+                       kAuthTagSize,
+                       kNonceSize,
+                       /* use_ietf_nonce_construction */ true) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/aes_128_gcm_encrypter.h b/quic/core/crypto/aes_128_gcm_encrypter.h
index c64c47a..0610eac 100644
--- a/quic/core/crypto/aes_128_gcm_encrypter.h
+++ b/quic/core/crypto/aes_128_gcm_encrypter.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
 #define QUICHE_QUIC_CORE_CRYPTO_AES_128_GCM_ENCRYPTER_H_
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -15,7 +15,7 @@
 //
 // It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
 // that is XOR'd with the packet number to compute the nonce.
-class QUIC_EXPORT_PRIVATE Aes128GcmEncrypter : public AeadBaseEncrypter {
+class QUIC_EXPORT_PRIVATE Aes128GcmEncrypter : public AesBaseEncrypter {
  public:
   enum {
     kAuthTagSize = 16,
diff --git a/quic/core/crypto/aes_128_gcm_encrypter_test.cc b/quic/core/crypto/aes_128_gcm_encrypter_test.cc
index 8ad6e20..3efe529 100644
--- a/quic/core/crypto/aes_128_gcm_encrypter_test.cc
+++ b/quic/core/crypto/aes_128_gcm_encrypter_test.cc
@@ -255,5 +255,20 @@
   EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
 }
 
+TEST_F(Aes128GcmEncrypterTest, GenerateHeaderProtectionMask) {
+  Aes128GcmEncrypter encrypter;
+  std::string key =
+      QuicTextUtils::HexDecode("d9132370cb18476ab833649cf080d970");
+  std::string sample =
+      QuicTextUtils::HexDecode("d1d7998068517adb769b48b924a32c47");
+  ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+  std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+  std::string expected_mask =
+      QuicTextUtils::HexDecode("b132c37d6164da4ea4dc9b763aceec27");
+  test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+                                      mask.size(), expected_mask.data(),
+                                      expected_mask.size());
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/crypto/aes_256_gcm_decrypter.cc b/quic/core/crypto/aes_256_gcm_decrypter.cc
index 08087a7..a5f09e5 100644
--- a/quic/core/crypto/aes_256_gcm_decrypter.cc
+++ b/quic/core/crypto/aes_256_gcm_decrypter.cc
@@ -19,11 +19,11 @@
 }  // namespace
 
 Aes256GcmDecrypter::Aes256GcmDecrypter()
-    : AeadBaseDecrypter(EVP_aead_aes_256_gcm,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ true) {
+    : AesBaseDecrypter(EVP_aead_aes_256_gcm,
+                       kKeySize,
+                       kAuthTagSize,
+                       kNonceSize,
+                       /* use_ietf_nonce_construction */ true) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/aes_256_gcm_decrypter.h b/quic/core/crypto/aes_256_gcm_decrypter.h
index bb398f1..260fece 100644
--- a/quic/core/crypto/aes_256_gcm_decrypter.h
+++ b/quic/core/crypto/aes_256_gcm_decrypter.h
@@ -7,7 +7,7 @@
 
 #include <cstdint>
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -17,7 +17,7 @@
 //
 // It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
 // that is XOR'd with the packet number to compute the nonce.
-class QUIC_EXPORT_PRIVATE Aes256GcmDecrypter : public AeadBaseDecrypter {
+class QUIC_EXPORT_PRIVATE Aes256GcmDecrypter : public AesBaseDecrypter {
  public:
   enum {
     kAuthTagSize = 16,
diff --git a/quic/core/crypto/aes_256_gcm_decrypter_test.cc b/quic/core/crypto/aes_256_gcm_decrypter_test.cc
index 6929ed0..4e7e05b 100644
--- a/quic/core/crypto/aes_256_gcm_decrypter_test.cc
+++ b/quic/core/crypto/aes_256_gcm_decrypter_test.cc
@@ -275,5 +275,21 @@
   }
 }
 
+TEST_F(Aes256GcmDecrypterTest, GenerateHeaderProtectionMask) {
+  Aes256GcmDecrypter decrypter;
+  std::string key = QuicTextUtils::HexDecode(
+      "ed23ecbf54d426def5c52c3dcfc84434e62e57781d3125bb21ed91b7d3e07788");
+  std::string sample =
+      QuicTextUtils::HexDecode("4d190c474be2b8babafb49ec4e38e810");
+  QuicDataReader sample_reader(sample.data(), sample.size());
+  ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+  std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+  std::string expected_mask =
+      QuicTextUtils::HexDecode("db9ed4e6ccd033af2eae01407199c56e");
+  test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+                                      mask.size(), expected_mask.data(),
+                                      expected_mask.size());
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/crypto/aes_256_gcm_encrypter.cc b/quic/core/crypto/aes_256_gcm_encrypter.cc
index b329f8e..146e3de 100644
--- a/quic/core/crypto/aes_256_gcm_encrypter.cc
+++ b/quic/core/crypto/aes_256_gcm_encrypter.cc
@@ -16,11 +16,11 @@
 }  // namespace
 
 Aes256GcmEncrypter::Aes256GcmEncrypter()
-    : AeadBaseEncrypter(EVP_aead_aes_256_gcm,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ true) {
+    : AesBaseEncrypter(EVP_aead_aes_256_gcm,
+                       kKeySize,
+                       kAuthTagSize,
+                       kNonceSize,
+                       /* use_ietf_nonce_construction */ true) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/aes_256_gcm_encrypter.h b/quic/core/crypto/aes_256_gcm_encrypter.h
index 0d93ac8..0e45822 100644
--- a/quic/core/crypto/aes_256_gcm_encrypter.h
+++ b/quic/core/crypto/aes_256_gcm_encrypter.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
 #define QUICHE_QUIC_CORE_CRYPTO_AES_256_GCM_ENCRYPTER_H_
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -15,7 +15,7 @@
 //
 // It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
 // that is XOR'd with the packet number to compute the nonce.
-class QUIC_EXPORT_PRIVATE Aes256GcmEncrypter : public AeadBaseEncrypter {
+class QUIC_EXPORT_PRIVATE Aes256GcmEncrypter : public AesBaseEncrypter {
  public:
   enum {
     kAuthTagSize = 16,
diff --git a/quic/core/crypto/aes_256_gcm_encrypter_test.cc b/quic/core/crypto/aes_256_gcm_encrypter_test.cc
index 71e6a53..86bf0c4 100644
--- a/quic/core/crypto/aes_256_gcm_encrypter_test.cc
+++ b/quic/core/crypto/aes_256_gcm_encrypter_test.cc
@@ -238,5 +238,20 @@
   EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
 }
 
+TEST_F(Aes256GcmEncrypterTest, GenerateHeaderProtectionMask) {
+  Aes256GcmEncrypter encrypter;
+  std::string key = QuicTextUtils::HexDecode(
+      "ed23ecbf54d426def5c52c3dcfc84434e62e57781d3125bb21ed91b7d3e07788");
+  std::string sample =
+      QuicTextUtils::HexDecode("4d190c474be2b8babafb49ec4e38e810");
+  ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+  std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+  std::string expected_mask =
+      QuicTextUtils::HexDecode("db9ed4e6ccd033af2eae01407199c56e");
+  test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+                                      mask.size(), expected_mask.data(),
+                                      expected_mask.size());
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/crypto/aes_base_decrypter.cc b/quic/core/crypto/aes_base_decrypter.cc
new file mode 100644
index 0000000..6888ab2
--- /dev/null
+++ b/quic/core/crypto/aes_base_decrypter.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_decrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aes.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool AesBaseDecrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+  if (key.size() != GetKeySize()) {
+    QUIC_BUG << "Invalid key size for header protection";
+    return false;
+  }
+  if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()),
+                          key.size() * 8, &pne_key_) != 0) {
+    QUIC_BUG << "Unexpected failure of AES_set_encrypt_key";
+    return false;
+  }
+  return true;
+}
+
+std::string AesBaseDecrypter::GenerateHeaderProtectionMask(
+    QuicDataReader* sample_reader) {
+  QuicStringPiece sample;
+  if (!sample_reader->ReadStringPiece(&sample, AES_BLOCK_SIZE)) {
+    return std::string();
+  }
+  std::string out(AES_BLOCK_SIZE, 0);
+  AES_encrypt(reinterpret_cast<const uint8_t*>(sample.data()),
+              reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+              &pne_key_);
+  return out;
+}
+
+}  // namespace quic
diff --git a/quic/core/crypto/aes_base_decrypter.h b/quic/core/crypto/aes_base_decrypter.h
new file mode 100644
index 0000000..e8e1cc8
--- /dev/null
+++ b/quic/core/crypto/aes_base_decrypter.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
+
+#include <cstddef>
+
+#include "third_party/boringssl/src/include/openssl/aes.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE AesBaseDecrypter : public AeadBaseDecrypter {
+ public:
+  using AeadBaseDecrypter::AeadBaseDecrypter;
+
+  bool SetHeaderProtectionKey(QuicStringPiece key) override;
+  std::string GenerateHeaderProtectionMask(
+      QuicDataReader* sample_reader) override;
+
+ private:
+  // The key used for packet number encryption.
+  AES_KEY pne_key_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_CRYPTO_AES_BASE_DECRYPTER_H_
diff --git a/quic/core/crypto/aes_base_encrypter.cc b/quic/core/crypto/aes_base_encrypter.cc
new file mode 100644
index 0000000..49fc71d
--- /dev/null
+++ b/quic/core/crypto/aes_base_encrypter.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/aes_base_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/aes.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool AesBaseEncrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+  if (key.size() != GetKeySize()) {
+    QUIC_BUG << "Invalid key size for header protection";
+    return false;
+  }
+  if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key.data()),
+                          key.size() * 8, &pne_key_) != 0) {
+    QUIC_BUG << "Unexpected failure of AES_set_encrypt_key";
+    return false;
+  }
+  return true;
+}
+
+std::string AesBaseEncrypter::GenerateHeaderProtectionMask(
+    QuicStringPiece sample) {
+  if (sample.size() != AES_BLOCK_SIZE) {
+    return std::string();
+  }
+  std::string out(AES_BLOCK_SIZE, 0);
+  AES_encrypt(reinterpret_cast<const uint8_t*>(sample.data()),
+              reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+              &pne_key_);
+  return out;
+}
+
+}  // namespace quic
diff --git a/quic/core/crypto/aes_base_encrypter.h b/quic/core/crypto/aes_base_encrypter.h
new file mode 100644
index 0000000..e115eba
--- /dev/null
+++ b/quic/core/crypto/aes_base_encrypter.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "third_party/boringssl/src/include/openssl/aes.h"
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE AesBaseEncrypter : public AeadBaseEncrypter {
+ public:
+  using AeadBaseEncrypter::AeadBaseEncrypter;
+
+  bool SetHeaderProtectionKey(QuicStringPiece key) override;
+  std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override;
+
+ private:
+  // The key used for packet number encryption.
+  AES_KEY pne_key_;
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_CRYPTO_AES_BASE_ENCRYPTER_H_
diff --git a/quic/core/crypto/chacha20_poly1305_decrypter.cc b/quic/core/crypto/chacha20_poly1305_decrypter.cc
index 83fd15e..e2f55aa 100644
--- a/quic/core/crypto/chacha20_poly1305_decrypter.cc
+++ b/quic/core/crypto/chacha20_poly1305_decrypter.cc
@@ -17,11 +17,11 @@
 }  // namespace
 
 ChaCha20Poly1305Decrypter::ChaCha20Poly1305Decrypter()
-    : AeadBaseDecrypter(EVP_aead_chacha20_poly1305,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ false) {
+    : ChaChaBaseDecrypter(EVP_aead_chacha20_poly1305,
+                          kKeySize,
+                          kAuthTagSize,
+                          kNonceSize,
+                          /* use_ietf_nonce_construction */ false) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/chacha20_poly1305_decrypter.h b/quic/core/crypto/chacha20_poly1305_decrypter.h
index 6e42556..50bb348 100644
--- a/quic/core/crypto/chacha20_poly1305_decrypter.h
+++ b/quic/core/crypto/chacha20_poly1305_decrypter.h
@@ -7,7 +7,7 @@
 
 #include <cstdint>
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -19,7 +19,8 @@
 //
 // It uses an authentication tag of 12 bytes (96 bits). The fixed prefix of the
 // nonce is four bytes.
-class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Decrypter : public AeadBaseDecrypter {
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Decrypter
+    : public ChaChaBaseDecrypter {
  public:
   enum {
     kAuthTagSize = 12,
diff --git a/quic/core/crypto/chacha20_poly1305_encrypter.cc b/quic/core/crypto/chacha20_poly1305_encrypter.cc
index eb51d83..37b899f 100644
--- a/quic/core/crypto/chacha20_poly1305_encrypter.cc
+++ b/quic/core/crypto/chacha20_poly1305_encrypter.cc
@@ -16,11 +16,11 @@
 }  // namespace
 
 ChaCha20Poly1305Encrypter::ChaCha20Poly1305Encrypter()
-    : AeadBaseEncrypter(EVP_aead_chacha20_poly1305,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ false) {
+    : ChaChaBaseEncrypter(EVP_aead_chacha20_poly1305,
+                          kKeySize,
+                          kAuthTagSize,
+                          kNonceSize,
+                          /* use_ietf_nonce_construction */ false) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/chacha20_poly1305_encrypter.h b/quic/core/crypto/chacha20_poly1305_encrypter.h
index e660bae..7200788 100644
--- a/quic/core/crypto/chacha20_poly1305_encrypter.h
+++ b/quic/core/crypto/chacha20_poly1305_encrypter.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
 #define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_ENCRYPTER_H_
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -17,7 +17,8 @@
 //
 // It uses an authentication tag of 12 bytes (96 bits). The fixed prefix of the
 // nonce is four bytes.
-class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Encrypter : public AeadBaseEncrypter {
+class QUIC_EXPORT_PRIVATE ChaCha20Poly1305Encrypter
+    : public ChaChaBaseEncrypter {
  public:
   enum {
     kAuthTagSize = 12,
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc b/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
index 4c10a53..8d98da8 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
+++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter.cc
@@ -19,11 +19,11 @@
 }  // namespace
 
 ChaCha20Poly1305TlsDecrypter::ChaCha20Poly1305TlsDecrypter()
-    : AeadBaseDecrypter(EVP_aead_chacha20_poly1305,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ true) {
+    : ChaChaBaseDecrypter(EVP_aead_chacha20_poly1305,
+                          kKeySize,
+                          kAuthTagSize,
+                          kNonceSize,
+                          /* use_ietf_nonce_construction */ true) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter.h b/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
index bffe8a2..702fb8c 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
+++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter.h
@@ -7,7 +7,7 @@
 
 #include <cstdint>
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -18,7 +18,7 @@
 // It uses an authentication tag of 16 bytes (128 bits). It uses a 12 bytes IV
 // that is XOR'd with the packet number to compute the nonce.
 class QUIC_EXPORT_PRIVATE ChaCha20Poly1305TlsDecrypter
-    : public AeadBaseDecrypter {
+    : public ChaChaBaseDecrypter {
  public:
   enum {
     kAuthTagSize = 16,
diff --git a/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc b/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
index 71b60e6..02ecc73 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
+++ b/quic/core/crypto/chacha20_poly1305_tls_decrypter_test.cc
@@ -167,5 +167,20 @@
   }
 }
 
+TEST_F(ChaCha20Poly1305TlsDecrypterTest, GenerateHeaderProtectionMask) {
+  ChaCha20Poly1305TlsDecrypter decrypter;
+  std::string key = QuicTextUtils::HexDecode(
+      "6a067f432787bd6034dd3f08f07fc9703a27e58c70e2d88d948b7f6489923cc7");
+  std::string sample =
+      QuicTextUtils::HexDecode("1210d91cceb45c716b023f492c29e612");
+  QuicDataReader sample_reader(sample.data(), sample.size());
+  ASSERT_TRUE(decrypter.SetHeaderProtectionKey(key));
+  std::string mask = decrypter.GenerateHeaderProtectionMask(&sample_reader);
+  std::string expected_mask = QuicTextUtils::HexDecode("1cc2cd98dc");
+  test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+                                      mask.size(), expected_mask.data(),
+                                      expected_mask.size());
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc b/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc
index c023ecb..c650b27 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc
+++ b/quic/core/crypto/chacha20_poly1305_tls_encrypter.cc
@@ -16,11 +16,11 @@
 }  // namespace
 
 ChaCha20Poly1305TlsEncrypter::ChaCha20Poly1305TlsEncrypter()
-    : AeadBaseEncrypter(EVP_aead_chacha20_poly1305,
-                        kKeySize,
-                        kAuthTagSize,
-                        kNonceSize,
-                        /* use_ietf_nonce_construction */ true) {
+    : ChaChaBaseEncrypter(EVP_aead_chacha20_poly1305,
+                          kKeySize,
+                          kAuthTagSize,
+                          kNonceSize,
+                          /* use_ietf_nonce_construction */ true) {
   static_assert(kKeySize <= kMaxKeySize, "key size too big");
   static_assert(kNonceSize <= kMaxNonceSize, "nonce size too big");
 }
diff --git a/quic/core/crypto/chacha20_poly1305_tls_encrypter.h b/quic/core/crypto/chacha20_poly1305_tls_encrypter.h
index 5bfcb5a..0ef7ae8 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_encrypter.h
+++ b/quic/core/crypto/chacha20_poly1305_tls_encrypter.h
@@ -5,7 +5,7 @@
 #ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
 #define QUICHE_QUIC_CORE_CRYPTO_CHACHA20_POLY1305_TLS_ENCRYPTER_H_
 
-#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 
 namespace quic {
@@ -16,7 +16,7 @@
 // It uses an authentication tag of 16 bytes (128 bits). It uses a 12 byte IV
 // that is XOR'd with the packet number to compute the nonce.
 class QUIC_EXPORT_PRIVATE ChaCha20Poly1305TlsEncrypter
-    : public AeadBaseEncrypter {
+    : public ChaChaBaseEncrypter {
  public:
   enum {
     kAuthTagSize = 16,
diff --git a/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc b/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
index 9c2a6ba..32fde50 100644
--- a/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
+++ b/quic/core/crypto/chacha20_poly1305_tls_encrypter_test.cc
@@ -152,5 +152,19 @@
   EXPECT_EQ(26u, encrypter.GetCiphertextSize(10));
 }
 
+TEST_F(ChaCha20Poly1305TlsEncrypterTest, GenerateHeaderProtectionMask) {
+  ChaCha20Poly1305TlsEncrypter encrypter;
+  std::string key = QuicTextUtils::HexDecode(
+      "6a067f432787bd6034dd3f08f07fc9703a27e58c70e2d88d948b7f6489923cc7");
+  std::string sample =
+      QuicTextUtils::HexDecode("1210d91cceb45c716b023f492c29e612");
+  ASSERT_TRUE(encrypter.SetHeaderProtectionKey(key));
+  std::string mask = encrypter.GenerateHeaderProtectionMask(sample);
+  std::string expected_mask = QuicTextUtils::HexDecode("1cc2cd98dc");
+  test::CompareCharArraysWithHexError("header protection mask", mask.data(),
+                                      mask.size(), expected_mask.data(),
+                                      expected_mask.size());
+}
+
 }  // namespace test
 }  // namespace quic
diff --git a/quic/core/crypto/chacha_base_decrypter.cc b/quic/core/crypto/chacha_base_decrypter.cc
new file mode 100644
index 0000000..e58fb1e
--- /dev/null
+++ b/quic/core/crypto/chacha_base_decrypter.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_decrypter.h"
+
+#include <cstdint>
+
+#include "third_party/boringssl/src/include/openssl/chacha.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool ChaChaBaseDecrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+  if (key.size() != GetKeySize()) {
+    QUIC_BUG << "Invalid key size for header protection";
+    return false;
+  }
+  memcpy(pne_key_, key.data(), key.size());
+  return true;
+}
+
+std::string ChaChaBaseDecrypter::GenerateHeaderProtectionMask(
+    QuicDataReader* sample_reader) {
+  QuicStringPiece sample;
+  if (!sample_reader->ReadStringPiece(&sample, 16)) {
+    return std::string();
+  }
+  const uint8_t* nonce = reinterpret_cast<const uint8_t*>(sample.data()) + 4;
+  uint32_t counter;
+  QuicDataReader(sample.data(), 4, Endianness::HOST_BYTE_ORDER)
+      .ReadUInt32(&counter);
+  const uint8_t zeroes[] = {0, 0, 0, 0, 0};
+  std::string out(arraysize(zeroes), 0);
+  CRYPTO_chacha_20(reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+                   zeroes, arraysize(zeroes), pne_key_, nonce, counter);
+  return out;
+}
+
+}  // namespace quic
diff --git a/quic/core/crypto/chacha_base_decrypter.h b/quic/core/crypto/chacha_base_decrypter.h
new file mode 100644
index 0000000..d7ad741
--- /dev/null
+++ b/quic/core/crypto/chacha_base_decrypter.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_decrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE ChaChaBaseDecrypter : public AeadBaseDecrypter {
+ public:
+  using AeadBaseDecrypter::AeadBaseDecrypter;
+
+  bool SetHeaderProtectionKey(QuicStringPiece key) override;
+  std::string GenerateHeaderProtectionMask(
+      QuicDataReader* sample_reader) override;
+
+ private:
+  // The key used for packet number encryption.
+  unsigned char pne_key_[kMaxKeySize];
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_DECRYPTER_H_
diff --git a/quic/core/crypto/chacha_base_encrypter.cc b/quic/core/crypto/chacha_base_encrypter.cc
new file mode 100644
index 0000000..24d79d5
--- /dev/null
+++ b/quic/core/crypto/chacha_base_encrypter.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/crypto/chacha_base_encrypter.h"
+
+#include "third_party/boringssl/src/include/openssl/chacha.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+
+namespace quic {
+
+bool ChaChaBaseEncrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+  if (key.size() != GetKeySize()) {
+    QUIC_BUG << "Invalid key size for header protection";
+    return false;
+  }
+  memcpy(pne_key_, key.data(), key.size());
+  return true;
+}
+
+std::string ChaChaBaseEncrypter::GenerateHeaderProtectionMask(
+    QuicStringPiece sample) {
+  if (sample.size() != 16) {
+    return std::string();
+  }
+  const uint8_t* nonce = reinterpret_cast<const uint8_t*>(sample.data()) + 4;
+  uint32_t counter;
+  QuicDataReader(sample.data(), 4, Endianness::HOST_BYTE_ORDER)
+      .ReadUInt32(&counter);
+  const uint8_t zeroes[] = {0, 0, 0, 0, 0};
+  std::string out(arraysize(zeroes), 0);
+  CRYPTO_chacha_20(reinterpret_cast<uint8_t*>(const_cast<char*>(out.data())),
+                   zeroes, arraysize(zeroes), pne_key_, nonce, counter);
+  return out;
+}
+
+}  // namespace quic
diff --git a/quic/core/crypto/chacha_base_encrypter.h b/quic/core/crypto/chacha_base_encrypter.h
new file mode 100644
index 0000000..7c086ac
--- /dev/null
+++ b/quic/core/crypto/chacha_base_encrypter.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
+#define QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/crypto/aead_base_encrypter.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QUIC_EXPORT_PRIVATE ChaChaBaseEncrypter : public AeadBaseEncrypter {
+ public:
+  using AeadBaseEncrypter::AeadBaseEncrypter;
+
+  bool SetHeaderProtectionKey(QuicStringPiece key) override;
+  std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override;
+
+ private:
+  // The key used for packet number encryption.
+  unsigned char pne_key_[kMaxKeySize];
+};
+
+}  // namespace quic
+
+#endif  // QUICHE_QUIC_CORE_CRYPTO_CHACHA_BASE_ENCRYPTER_H_
diff --git a/quic/core/crypto/null_decrypter.cc b/quic/core/crypto/null_decrypter.cc
index 288d3c4..4934f62 100644
--- a/quic/core/crypto/null_decrypter.cc
+++ b/quic/core/crypto/null_decrypter.cc
@@ -28,6 +28,10 @@
   return iv.empty();
 }
 
+bool NullDecrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+  return key.empty();
+}
+
 bool NullDecrypter::SetPreliminaryKey(QuicStringPiece key) {
   QUIC_BUG << "Should not be called";
   return false;
@@ -66,6 +70,11 @@
   return true;
 }
 
+std::string NullDecrypter::GenerateHeaderProtectionMask(
+    QuicDataReader* sample_reader) {
+  return std::string(5, 0);
+}
+
 size_t NullDecrypter::GetKeySize() const {
   return 0;
 }
diff --git a/quic/core/crypto/null_decrypter.h b/quic/core/crypto/null_decrypter.h
index 433c959..06c361d 100644
--- a/quic/core/crypto/null_decrypter.h
+++ b/quic/core/crypto/null_decrypter.h
@@ -32,6 +32,7 @@
   bool SetKey(QuicStringPiece key) override;
   bool SetNoncePrefix(QuicStringPiece nonce_prefix) override;
   bool SetIV(QuicStringPiece iv) override;
+  bool SetHeaderProtectionKey(QuicStringPiece key) override;
   bool SetPreliminaryKey(QuicStringPiece key) override;
   bool SetDiversificationNonce(const DiversificationNonce& nonce) override;
   bool DecryptPacket(uint64_t packet_number,
@@ -40,6 +41,8 @@
                      char* output,
                      size_t* output_length,
                      size_t max_output_length) override;
+  std::string GenerateHeaderProtectionMask(
+      QuicDataReader* sample_reader) override;
   size_t GetKeySize() const override;
   size_t GetIVSize() const override;
   QuicStringPiece GetKey() const override;
diff --git a/quic/core/crypto/null_encrypter.cc b/quic/core/crypto/null_encrypter.cc
index 9819a31..69db96d 100644
--- a/quic/core/crypto/null_encrypter.cc
+++ b/quic/core/crypto/null_encrypter.cc
@@ -26,6 +26,10 @@
   return iv.empty();
 }
 
+bool NullEncrypter::SetHeaderProtectionKey(QuicStringPiece key) {
+  return key.empty();
+}
+
 bool NullEncrypter::EncryptPacket(uint64_t /*packet_number*/,
                                   QuicStringPiece associated_data,
                                   QuicStringPiece plaintext,
@@ -53,6 +57,11 @@
   return true;
 }
 
+std::string NullEncrypter::GenerateHeaderProtectionMask(
+    QuicStringPiece sample) {
+  return std::string(5, 0);
+}
+
 size_t NullEncrypter::GetKeySize() const {
   return 0;
 }
diff --git a/quic/core/crypto/null_encrypter.h b/quic/core/crypto/null_encrypter.h
index 773cfdd..efd6e23 100644
--- a/quic/core/crypto/null_encrypter.h
+++ b/quic/core/crypto/null_encrypter.h
@@ -28,12 +28,14 @@
   bool SetKey(QuicStringPiece key) override;
   bool SetNoncePrefix(QuicStringPiece nonce_prefix) override;
   bool SetIV(QuicStringPiece iv) override;
+  bool SetHeaderProtectionKey(QuicStringPiece key) override;
   bool EncryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece plaintext,
                      char* output,
                      size_t* output_length,
                      size_t max_output_length) override;
+  std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override;
   size_t GetKeySize() const override;
   size_t GetNoncePrefixSize() const override;
   size_t GetIVSize() const override;
diff --git a/quic/core/crypto/quic_crypter.h b/quic/core/crypto/quic_crypter.h
index c413c4c..c698dfb 100644
--- a/quic/core/crypto/quic_crypter.h
+++ b/quic/core/crypto/quic_crypter.h
@@ -69,6 +69,9 @@
   // packet number, even when retransmitting a lost packet.
   virtual bool SetIV(QuicStringPiece iv) = 0;
 
+  // Sets the key to use for header protection.
+  virtual bool SetHeaderProtectionKey(QuicStringPiece key) = 0;
+
   // Returns the size in bytes of a key for the algorithm.
   virtual size_t GetKeySize() const = 0;
   // Returns the size in bytes of an IV to use with the algorithm.
diff --git a/quic/core/crypto/quic_decrypter.h b/quic/core/crypto/quic_decrypter.h
index a2b104c..559420c 100644
--- a/quic/core/crypto/quic_decrypter.h
+++ b/quic/core/crypto/quic_decrypter.h
@@ -11,6 +11,7 @@
 #include <string>
 
 #include "net/third_party/quiche/src/quic/core/crypto/quic_crypter.h"
+#include "net/third_party/quiche/src/quic/core/quic_data_reader.h"
 #include "net/third_party/quiche/src/quic/core/quic_packets.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
@@ -60,6 +61,13 @@
                              size_t* output_length,
                              size_t max_output_length) = 0;
 
+  // Reads a sample of ciphertext from |sample_reader| and uses the header
+  // protection key to generate a mask to use for header protection. If
+  // successful, this function returns this mask, which is at least 5 bytes
+  // long. Callers can detect failure by checking if the output string is empty.
+  virtual std::string GenerateHeaderProtectionMask(
+      QuicDataReader* sample_reader) = 0;
+
   // The ID of the cipher. Return 0x03000000 ORed with the 'cryptographic suite
   // selector'.
   virtual uint32_t cipher_id() const = 0;
diff --git a/quic/core/crypto/quic_encrypter.h b/quic/core/crypto/quic_encrypter.h
index cb53e33..6a69fcc 100644
--- a/quic/core/crypto/quic_encrypter.h
+++ b/quic/core/crypto/quic_encrypter.h
@@ -41,6 +41,12 @@
                              size_t* output_length,
                              size_t max_output_length) = 0;
 
+  // Takes a |sample| of ciphertext and uses the header protection key to
+  // generate a mask to use for header protection, and returns that mask. On
+  // success, the mask will be at least 5 bytes long; on failure the string will
+  // be empty.
+  virtual std::string GenerateHeaderProtectionMask(QuicStringPiece sample) = 0;
+
   // GetKeySize() and GetNoncePrefixSize() tell the HKDF class how many bytes
   // of key material needs to be derived from the master secret.
   // NOTE: the sizes returned by GetKeySize() and GetNoncePrefixSize() are
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc
index 00a966d..d2b559a 100644
--- a/quic/core/quic_connection_test.cc
+++ b/quic/core/quic_connection_test.cc
@@ -127,6 +127,8 @@
 
   bool SetIV(QuicStringPiece iv) override { return true; }
 
+  bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; }
+
   bool EncryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece plaintext,
@@ -145,6 +147,10 @@
     return true;
   }
 
+  std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override {
+    return std::string(5, 0);
+  }
+
   size_t GetKeySize() const override { return 0; }
   size_t GetNoncePrefixSize() const override { return 0; }
   size_t GetIVSize() const override { return 0; }
@@ -182,6 +188,8 @@
 
   bool SetIV(QuicStringPiece iv) override { return true; }
 
+  bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; }
+
   bool SetPreliminaryKey(QuicStringPiece key) override {
     QUIC_BUG << "should not be called";
     return false;
@@ -208,6 +216,11 @@
     return true;
   }
 
+  std::string GenerateHeaderProtectionMask(
+      QuicDataReader* sample_reader) override {
+    return std::string(5, 0);
+  }
+
   size_t GetKeySize() const override { return 0; }
   size_t GetIVSize() const override { return 0; }
   QuicStringPiece GetKey() const override { return QuicStringPiece(); }
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc
index 360aa47..8519510 100644
--- a/quic/core/quic_framer_test.cc
+++ b/quic/core/quic_framer_test.cc
@@ -91,6 +91,7 @@
   bool SetKey(QuicStringPiece key) override { return true; }
   bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; }
   bool SetIV(QuicStringPiece iv) override { return true; }
+  bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; }
   bool EncryptPacket(uint64_t packet_number,
                      QuicStringPiece associated_data,
                      QuicStringPiece plaintext,
@@ -104,6 +105,9 @@
     *output_length = plaintext.length();
     return true;
   }
+  std::string GenerateHeaderProtectionMask(QuicStringPiece sample) override {
+    return std::string(5, 0);
+  }
   size_t GetKeySize() const override { return 0; }
   size_t GetNoncePrefixSize() const override { return 0; }
   size_t GetIVSize() const override { return 0; }
@@ -127,6 +131,7 @@
   bool SetKey(QuicStringPiece key) override { return true; }
   bool SetNoncePrefix(QuicStringPiece nonce_prefix) override { return true; }
   bool SetIV(QuicStringPiece iv) override { return true; }
+  bool SetHeaderProtectionKey(QuicStringPiece key) override { return true; }
   bool SetPreliminaryKey(QuicStringPiece key) override {
     QUIC_BUG << "should not be called";
     return false;
@@ -147,6 +152,10 @@
     *output_length = ciphertext.length();
     return true;
   }
+  std::string GenerateHeaderProtectionMask(
+      QuicDataReader* sample_reader) override {
+    return std::string(5, 0);
+  }
   size_t GetKeySize() const override { return 0; }
   size_t GetIVSize() const override { return 0; }
   QuicStringPiece GetKey() const override { return QuicStringPiece(); }