1 /*
2  *  Copyright (c) 2018-present, Facebook, Inc.
3  *  All rights reserved.
4  *
5  *  This source code is licensed under the BSD-style license found in the
6  *  LICENSE file in the root directory of this source tree.
7  */
8 
9 #include <gtest/gtest.h>
10 
11 #include <fizz/crypto/Sha256.h>
12 #include <fizz/crypto/aead/test/TestUtil.h>
13 #include <fizz/crypto/hpke/Context.h>
14 #include <fizz/crypto/hpke/Utils.h>
15 #include <fizz/crypto/hpke/test/Mocks.h>
16 #include <fizz/crypto/test/TestUtil.h>
17 #include <fizz/protocol/Types.h>
18 #include <fizz/record/Types.h>
19 
20 using namespace fizz::test;
21 
22 namespace fizz {
23 namespace hpke {
24 namespace test {
25 
26 const std::string kExportSecret =
27     "60f5fe76e2699f98c19eab82fecf330b990ac32694a8e40e598e2326d0e29150";
28 
29 struct Params {
30   std::string key;
31   std::string iv;
32   std::string aad;
33   std::string plaintext;
34   std::string ciphertext;
35   CipherSuite cipher;
36   std::string exporterSecret;
37   std::string exportContext;
38   std::string expectedExportValue;
39 };
40 
41 class HpkeContextTest : public ::testing::TestWithParam<Params> {};
42 
TEST_P(HpkeContextTest,TestContext)43 TEST_P(HpkeContextTest, TestContext) {
44   auto testParam = GetParam();
45   const auto kPrefix = folly::IOBuf::copyBuffer("HPKE-07");
46   auto suiteId = generateHpkeSuiteId(
47       NamedGroup::secp256r1, HashFunction::Sha256, testParam.cipher);
48   auto encryptCipher = getCipher(testParam.cipher);
49   encryptCipher->setKey(
50       TrafficKey{toIOBuf(testParam.key), toIOBuf(testParam.iv)});
51 
52   HpkeContext encryptContext(
53       std::move(encryptCipher),
54       toIOBuf(kExportSecret),
55       std::make_unique<fizz::hpke::Hkdf>(
56           kPrefix->clone(),
57           std::make_unique<HkdfImpl>(HkdfImpl::create<Sha256>())),
58       suiteId->clone(),
59       fizz::hpke::HpkeContext::Role::Sender);
60   auto gotCiphertext = encryptContext.seal(
61       toIOBuf(testParam.aad).get(), toIOBuf(testParam.plaintext));
62   EXPECT_TRUE(
63       folly::IOBufEqualTo()(gotCiphertext, toIOBuf(testParam.ciphertext)));
64 
65   auto decryptCipher = getCipher(testParam.cipher);
66   decryptCipher->setKey(
67       TrafficKey{toIOBuf(testParam.key), toIOBuf(testParam.iv)});
68   HpkeContext decryptContext(
69       std::move(decryptCipher),
70       toIOBuf(kExportSecret),
71       std::make_unique<fizz::hpke::Hkdf>(
72           kPrefix->clone(),
73           std::make_unique<HkdfImpl>(HkdfImpl::create<Sha256>())),
74       std::move(suiteId),
75       fizz::hpke::HpkeContext::Role::Receiver);
76   auto gotPlaintext = decryptContext.open(
77       toIOBuf(testParam.aad).get(), std::move(gotCiphertext));
78   EXPECT_TRUE(
79       folly::IOBufEqualTo()(gotPlaintext, toIOBuf(testParam.plaintext)));
80 }
81 
TEST_P(HpkeContextTest,TestContextRoles)82 TEST_P(HpkeContextTest, TestContextRoles) {
83   auto testParam = GetParam();
84   const auto kPrefix = folly::IOBuf::copyBuffer("HPKE-07");
85   auto suiteId = generateHpkeSuiteId(
86       NamedGroup::secp256r1, HashFunction::Sha256, testParam.cipher);
87   auto encryptCipher = getCipher(testParam.cipher);
88   encryptCipher->setKey(
89       TrafficKey{toIOBuf(testParam.key), toIOBuf(testParam.iv)});
90 
91   HpkeContext encryptContext(
92       std::move(encryptCipher),
93       toIOBuf(kExportSecret),
94       std::make_unique<fizz::hpke::Hkdf>(
95           kPrefix->clone(),
96           std::make_unique<HkdfImpl>(HkdfImpl::create<Sha256>())),
97       suiteId->clone(),
98       fizz::hpke::HpkeContext::Role::Sender);
99 
100   auto decryptCipher = getCipher(testParam.cipher);
101   decryptCipher->setKey(
102       TrafficKey{toIOBuf(testParam.key), toIOBuf(testParam.iv)});
103   HpkeContext decryptContext(
104       std::move(decryptCipher),
105       toIOBuf(kExportSecret),
106       std::make_unique<fizz::hpke::Hkdf>(
107           kPrefix->clone(),
108           std::make_unique<HkdfImpl>(HkdfImpl::create<Sha256>())),
109       std::move(suiteId),
110       fizz::hpke::HpkeContext::Role::Receiver);
111 
112   auto gotCiphertext = encryptContext.seal(
113       toIOBuf(testParam.aad).get(), toIOBuf(testParam.plaintext));
114 
115   // Check that encrypting/decrypting from the wrong role errors
116   EXPECT_THROW(
117       decryptContext.seal(
118           toIOBuf(testParam.aad).get(), toIOBuf(testParam.plaintext)),
119       std::logic_error);
120   EXPECT_THROW(
121       encryptContext.open(
122           toIOBuf(testParam.aad).get(), std::move(gotCiphertext)),
123       std::logic_error);
124 }
125 
TEST_P(HpkeContextTest,TestExportSecret)126 TEST_P(HpkeContextTest, TestExportSecret) {
127   for (auto role :
128        {fizz::hpke::HpkeContext::Role::Sender,
129         fizz::hpke::HpkeContext::Role::Receiver}) {
130     auto testParam = GetParam();
131     const auto kPrefix = folly::IOBuf::copyBuffer("HPKE-07");
132     auto exporterContext = toIOBuf(testParam.exportContext);
133 
134     auto suiteId = generateHpkeSuiteId(
135         NamedGroup::x25519, HashFunction::Sha256, testParam.cipher);
136     HpkeContext context(
137         getCipher(testParam.cipher),
138         toIOBuf(testParam.exporterSecret),
139         std::make_unique<fizz::hpke::Hkdf>(
140             kPrefix->clone(),
141             std::make_unique<HkdfImpl>(HkdfImpl::create<Sha256>())),
142         std::move(suiteId),
143         role);
144     auto secret = context.exportSecret(std::move(exporterContext), 32);
145 
146     auto expectedValue = folly::unhexlify(testParam.expectedExportValue);
147     EXPECT_TRUE(
148         folly::IOBufEqualTo()(secret, folly::IOBuf::copyBuffer(expectedValue)));
149   }
150 }
151 
TEST_P(HpkeContextTest,TestExportSecretThrow)152 TEST_P(HpkeContextTest, TestExportSecretThrow) {
153   for (auto role :
154        {fizz::hpke::HpkeContext::Role::Sender,
155         fizz::hpke::HpkeContext::Role::Receiver}) {
156     auto testParam = GetParam();
157     const auto kPrefix = folly::IOBuf::copyBuffer("HPKE-07");
158     auto exporterContext = toIOBuf(testParam.exportContext);
159 
160     auto suiteId = generateHpkeSuiteId(
161         NamedGroup::x25519,
162         HashFunction::Sha256,
163         CipherSuite::TLS_AES_128_GCM_SHA256);
164     HpkeContext context(
165         OpenSSLEVPCipher::makeCipher<AESGCM128>(),
166         toIOBuf(testParam.exporterSecret),
167         std::make_unique<fizz::hpke::Hkdf>(
168             kPrefix->clone(),
169             std::make_unique<HkdfImpl>(HkdfImpl::create<Sha256>())),
170         std::move(suiteId),
171         role);
172 
173     EXPECT_THROW(
174         context.exportSecret(std::move(exporterContext), SIZE_MAX),
175         std::runtime_error);
176   }
177 }
178 
179 /***
180  * Test vectors sourced from HPKE IETF draft vectors
181  * https://raw.githubusercontent.com/cfrg/draft-irtf-cfrg-hpke/3d6ced124134825ed7a953b126cf5f756d960bc9/test-vectors.json
182  */
183 // clang-format off
184 
185 INSTANTIATE_TEST_CASE_P(
186     TestVectors,
187     HpkeContextTest,
188     ::testing::
189         Values(
190             Params{
191                 "e20cee1bf5392ad2d3a442e231f187ae",
192                 "5d99b2f03c452f7a9441933a",
193                 "436f756e742d30",
194                 "4265617574792069732074727574682c20747275746820626561757479",
195                 "9418f1ae06eddc43aa911032aed4a951754ee2286a786733761857f8d96a7ec8d852da93bc5eeab49623344aba",
196                 CipherSuite::TLS_AES_128_GCM_SHA256,
197                 "00c3cdacab28e981cc907d12e4f55f0aacae261dbb4eb610447a6bc431bfe2aa",
198                 "54657374436f6e74657874",
199                 "c8387c1e6ec4f026c7f3577e3f29df51f46161295eec84c4f64a9174f7b64e4f"},
200             Params{
201                 "29b6985e93a71d68d77935d8372cf179db14bc21a3ad681e3afcabd287e46fd0",
202                 "52d2af88623a97733e068886",
203                 "436f756e742d30",
204                 "4265617574792069732074727574682c20747275746820626561757479",
205                 "451972846bb2c58ff6e6eb5ccc3bda8cc84fb6e93be5a1119cc32e3e374182d66d9a5910a14ec51baede71bedf",
206                 CipherSuite::TLS_AES_256_GCM_SHA384,
207                 "29f070f3562ed755db05a24bd3ce1562f7cee293cc5531bbc9573863731566b3",
208                 "54657374436f6e74657874",
209                 "36c126f8cb75015204ea8ceb866b346fa33309f3723553b91eae547e15153d72"},
210             Params{
211                 "a17448a542d0d6d75e3b21be0a1f68607904b4802c6b19a7e7e90976aa00a5c8",
212                 "6f6b832dba944a91e5684514",
213                 "436f756e742d30",
214                 "4265617574792069732074727574682c20747275746820626561757479",
215                 "1b9ce69bd0e6b4242ac2dd841ef093fc9dfa9e684f81c2d1778fd3268ca5aa7d612cd87f72acd2aeaee084dee2",
216                 CipherSuite::TLS_CHACHA20_POLY1305_SHA256,
217                 "bbbd4216184bd12888e0cec08e384c2e39639fe1527f220f3aa751f5290a9aa7",
218                 "54657374436f6e74657874",
219                 "bb69068c4f7767331512d375e4ab0ca0c6c51446040096ea0ae1cc3f9a3f54bd"}
220     ));
221 // clang-format on
222 
223 } // namespace test
224 } // namespace hpke
225 } // namespace fizz
226