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