proxygen
ExportedAuthenticator.cpp
Go to the documentation of this file.
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 
11 
12 using namespace folly;
13 
14 namespace fizz {
15 
16 Buf ExportedAuthenticator::getAuthenticatorRequest(
17  Buf certificateRequestContext,
18  std::vector<fizz::Extension> extensions) {
19  if (!certificateRequestContext || certificateRequestContext->empty()) {
20  throw FizzException(
21  "certificate request context must not be empty",
22  AlertDescription::illegal_parameter);
23  }
24 
26  cr.certificate_request_context = std::move(certificateRequestContext);
27  cr.extensions = std::move(extensions);
29 }
30 
31 Buf ExportedAuthenticator::getAuthenticator(
32  const fizz::AsyncFizzBase& transport,
33  Direction dir,
34  const SelfCert& cert,
35  Buf authenticatorRequest) {
36  auto cipher = transport.getCipher();
37  auto deriver = Factory().makeKeyDeriver(*cipher);
38  auto hashLength = deriver->hashLength();
39  auto supportedSchemes = transport.getSupportedSigSchemes();
40  Buf handshakeContext;
41  Buf finishedMacKey;
42  if (dir == Direction::UPSTREAM) {
43  handshakeContext = transport.getEkm(
44  "EXPORTER-client authenticator handshake context", nullptr, hashLength);
45  finishedMacKey = transport.getEkm(
46  "EXPORTER-client authenticator finished key", nullptr, hashLength);
47  } else {
48  handshakeContext = transport.getEkm(
49  "EXPORTER-server authenticator handshake context", nullptr, hashLength);
50  finishedMacKey = transport.getEkm(
51  "EXPORTER-server authenticator finished key", nullptr, hashLength);
52  }
53  return makeAuthenticator(
54  deriver,
55  supportedSchemes,
56  cert,
57  std::move(authenticatorRequest),
58  std::move(handshakeContext),
59  std::move(finishedMacKey),
60  CertificateVerifyContext::Authenticator);
61 }
62 
63 Buf ExportedAuthenticator::getAuthenticatorContext(Buf authenticator) {
65  authQueue.append(std::move(authenticator));
66  auto param = fizz::ReadRecordLayer::decodeHandshakeMessage(authQueue);
67  auto& certMsg = boost::get<CertificateMsg>(*param);
68  return std::move(certMsg.certificate_request_context);
69 }
70 
72 ExportedAuthenticator::validateAuthenticator(
73  const fizz::AsyncFizzBase& transport,
74  Direction dir,
75  Buf authenticatorRequest,
76  Buf authenticator) {
77  auto cipher = transport.getCipher();
78  auto deriver = Factory().makeKeyDeriver(*cipher);
79  auto hashLength = deriver->hashLength();
80  Buf handshakeContext;
81  Buf finishedMacKey;
82  if (dir == Direction::UPSTREAM) {
83  handshakeContext = transport.getEkm(
84  "EXPORTER-server authenticator handshake context", nullptr, hashLength);
85  finishedMacKey = transport.getEkm(
86  "EXPORTER-server authenticator finished key", nullptr, hashLength);
87  } else {
88  handshakeContext = transport.getEkm(
89  "EXPORTER-client authenticator handshake context", nullptr, hashLength);
90  finishedMacKey = transport.getEkm(
91  "EXPORTER-client authenticator finished key", nullptr, hashLength);
92  }
93  auto certs = validate(
94  deriver,
95  std::move(authenticatorRequest),
96  std::move(authenticator),
97  std::move(handshakeContext),
98  std::move(finishedMacKey),
99  CertificateVerifyContext::Authenticator);
100  return certs;
101 }
102 
103 Buf ExportedAuthenticator::makeAuthenticator(
104  std::unique_ptr<KeyDerivation>& kderiver,
105  std::vector<SignatureScheme> supportedSchemes,
106  const SelfCert& cert,
107  Buf authenticatorRequest,
108  Buf handshakeContext,
109  Buf finishedMacKey,
111  Buf certificateRequestContext;
112  std::vector<fizz::Extension> extensions;
113  std::tie(certificateRequestContext, extensions) =
114  detail::decodeAuthRequest(authenticatorRequest);
116  detail::getSignatureScheme(supportedSchemes, cert, extensions);
117  // No proper signature scheme could be selected, return an empty
118  // authenticator.
119  if (!scheme) {
120  auto emptyAuth = detail::getEmptyAuthenticator(
121  kderiver,
122  std::move(authenticatorRequest),
123  std::move(handshakeContext),
124  std::move(finishedMacKey));
125  return emptyAuth;
126  }
127  // Compute CertificateMsg.
129  cert.getCertMessage(std::move(certificateRequestContext));
130  auto encodedCertMsg = encodeHandshake(std::move(certificate));
131  // Compute CertificateVerify.
132  auto transcript = detail::computeTranscript(
133  handshakeContext, authenticatorRequest, encodedCertMsg);
134  auto transcriptHash = detail::computeTranscriptHash(kderiver, transcript);
135  auto sig = cert.sign(*scheme, context, transcriptHash->coalesce());
137  verify.algorithm = *scheme;
138  verify.signature = std::move(sig);
139  auto encodedCertificateVerify = encodeHandshake(std::move(verify));
140  // Compute Finished.
141  auto finishedTranscript =
142  detail::computeFinishedTranscript(transcript, encodedCertificateVerify);
143  auto finishedTranscriptHash =
144  detail::computeTranscriptHash(kderiver, finishedTranscript);
145  auto verifyData =
146  detail::getFinishedData(kderiver, finishedMacKey, finishedTranscriptHash);
148  finished.verify_data = std::move(verifyData);
149  auto encodedFinished = encodeHandshake(std::move(finished));
150 
152  encodedCertMsg, encodedCertificateVerify, encodedFinished);
153 }
154 
155 folly::Optional<std::vector<CertificateEntry>> ExportedAuthenticator::validate(
156  std::unique_ptr<KeyDerivation>& kderiver,
157  Buf authenticatorRequest,
158  Buf authenticator,
159  Buf handshakeContext,
160  Buf finishedMacKey,
164  constexpr uint16_t capacity = 256;
165  // Clone the authenticator which is later compared to the re-calculated empty
166  // authenticator.
167  auto authClone = authenticator->clone();
168  authQueue.append(std::move(authenticator));
169  auto param = fizz::ReadRecordLayer::decodeHandshakeMessage(authQueue);
170  if (!param) {
171  return folly::none;
172  }
173  // First check if the authenticator is empty.
174  auto finished = boost::get<Finished>(&(*param));
175  if (finished) {
176  auto emptyAuth = detail::getEmptyAuthenticator(
177  kderiver,
178  std::move(authenticatorRequest),
179  std::move(handshakeContext),
180  std::move(finishedMacKey));
181  if (folly::IOBufEqualTo()(emptyAuth, authClone)) {
182  return std::vector<CertificateEntry>();
183  } else {
184  return folly::none;
185  }
186  }
187  auto param2 = fizz::ReadRecordLayer::decodeHandshakeMessage(authQueue);
188  if (!param2) {
189  return folly::none;
190  }
191  auto param3 = fizz::ReadRecordLayer::decodeHandshakeMessage(authQueue);
192  if (!param3) {
193  return folly::none;
194  }
195  auto certMsg = boost::get<CertificateMsg>(&(*param));
196  auto certVerify = boost::get<CertificateVerify>(&(*param2));
197  finished = boost::get<Finished>(&(*param3));
198  if (!certMsg || !certVerify || !finished) {
199  return folly::none;
200  }
201 
202  auto leafCert = folly::IOBuf::create(capacity);
203  folly::io::Appender appender(leafCert.get(), capacity);
204  detail::writeBuf(certMsg->certificate_list.front().cert_data, appender);
205  auto peerCert = CertUtils::makePeerCert(std::move(leafCert));
206  auto encodedCertMsg = encodeHandshake(std::move(*certMsg));
207  auto transcript = detail::computeTranscript(
208  handshakeContext, authenticatorRequest, encodedCertMsg);
209  auto transcriptHash = detail::computeTranscriptHash(kderiver, transcript);
210  try {
211  peerCert->verify(
212  certVerify->algorithm,
213  context,
214  transcriptHash->coalesce(),
215  certVerify->signature->coalesce());
216  } catch (const std::runtime_error&) {
217  return folly::none;
218  }
219  // Verify if Finished message matches.
220  auto encodedCertVerify = encodeHandshake(std::move(*certVerify));
221  auto finishedTranscript =
223  auto finishedTranscriptHash =
224  detail::computeTranscriptHash(kderiver, finishedTranscript);
225  auto verifyData =
226  detail::getFinishedData(kderiver, finishedMacKey, finishedTranscriptHash);
227 
228  if (folly::IOBufEqualTo()(finished->verify_data, verifyData)) {
229  certs = std::move(certMsg->certificate_list);
230  return certs;
231  } else {
232  return folly::none;
233  }
234 }
235 
236 namespace detail {
237 
238 std::tuple<Buf, std::vector<fizz::Extension>> decodeAuthRequest(
239  const Buf& authRequest) {
240  Buf certRequestContext;
241  std::vector<fizz::Extension> exts;
242  if (authRequest && !(authRequest->empty())) {
243  folly::io::Cursor cursor(authRequest.get());
244  CertificateRequest decodedCertRequest = decode<CertificateRequest>(cursor);
245  certRequestContext =
246  std::move(decodedCertRequest.certificate_request_context);
247  exts = std::move(decodedCertRequest.extensions);
248  } else {
249  certRequestContext = folly::IOBuf::copyBuffer("");
250  }
251  return std::make_tuple(std::move(certRequestContext), std::move(exts));
252 }
253 
255  std::unique_ptr<KeyDerivation>& deriver,
256  const Buf& toBeHashed) {
257  auto hashLength = deriver->hashLength();
258  auto data = folly::IOBuf::create(hashLength);
259  data->append(hashLength);
260  auto transcriptHash =
261  folly::MutableByteRange(data->writableData(), data->length());
262  deriver->hash(*toBeHashed, transcriptHash);
263  return data;
264 }
265 
266 void writeBuf(const Buf& buf, folly::io::Appender& out) {
267  if (buf && !(buf->empty())) {
268  auto current = buf.get();
269  size_t chainElements = buf->countChainElements();
270  for (size_t i = 0; i < chainElements; ++i) {
271  out.push(current->data(), current->length());
272  current = current->next();
273  }
274  }
275 }
276 
278  const Buf& handshakeContext,
279  const Buf& authenticatorRequest,
280  const Buf& certificate) {
281  constexpr uint16_t capacity = 256;
282  auto out = folly::IOBuf::create(capacity);
283  folly::io::Appender appender(out.get(), capacity);
284  detail::writeBuf(handshakeContext, appender);
285  detail::writeBuf(authenticatorRequest, appender);
286  detail::writeBuf(certificate, appender);
287  return out;
288 }
289 
290 Buf computeFinishedTranscript(const Buf& crTranscript, const Buf& certVerify) {
291  constexpr uint16_t capacity = 256;
292  auto out = folly::IOBuf::create(capacity);
293  folly::io::Appender appender(out.get(), capacity);
294  detail::writeBuf(crTranscript, appender);
295  detail::writeBuf(certVerify, appender);
296  return out;
297 }
298 
300  std::unique_ptr<KeyDerivation>& deriver,
301  Buf& finishedMacKey,
302  const Buf& finishedTranscript) {
303  auto hashLength = deriver->hashLength();
304  auto data = folly::IOBuf::create(hashLength);
305  data->append(hashLength);
306  auto outRange = folly::MutableByteRange(data->writableData(), data->length());
307  deriver->hmac(finishedMacKey->coalesce(), *finishedTranscript, outRange);
308  return data;
309 }
310 
312  const std::vector<fizz::Extension>& authRequestExtensions) {
313  if (!(authRequestExtensions.empty())) {
314  auto sigAlgsExtension =
315  getExtension<SignatureAlgorithms>(authRequestExtensions);
316  if (sigAlgsExtension) {
317  auto requestedSchemes = sigAlgsExtension->supported_signature_algorithms;
318  return requestedSchemes;
319  } else {
320  return folly::none;
321  }
322  } else {
323  return folly::none;
324  }
325 }
326 
328  const std::vector<SignatureScheme>& supportedSchemes,
329  const SelfCert& cert,
330  const std::vector<fizz::Extension>& authRequestExtensions) {
331  folly::Optional<SignatureScheme> selectedScheme;
332  const auto certSchemes = cert.getSigSchemes();
334  getRequestedSchemes(authRequestExtensions);
335  if (requestedSchemes) {
336  for (const auto& scheme : supportedSchemes) {
337  if (std::find(certSchemes.begin(), certSchemes.end(), scheme) !=
338  certSchemes.end() &&
339  std::find(
340  (*requestedSchemes).begin(), (*requestedSchemes).end(), scheme) !=
341  (*requestedSchemes).end()) {
342  selectedScheme = scheme;
343  break;
344  }
345  }
346  }
347 
348  if (!selectedScheme) {
349  VLOG(1) << "authenticator request without proper signature algorithms";
350  for (const auto& scheme : supportedSchemes) {
351  if (std::find(certSchemes.begin(), certSchemes.end(), scheme) !=
352  certSchemes.end()) {
353  selectedScheme = scheme;
354  break;
355  }
356  }
357  }
358  return selectedScheme;
359 }
360 
362  std::unique_ptr<KeyDerivation>& kderiver,
363  Buf authRequest,
364  Buf handshakeContext,
365  Buf finishedMacKey) {
366  CertificateMsg emptyCertMsg;
367  emptyCertMsg.certificate_request_context =
368  std::get<0>(detail::decodeAuthRequest(authRequest));
369  auto encodedEmptyCertMsg = encodeHandshake(std::move(emptyCertMsg));
370  auto emptyAuthTranscript = detail::computeTranscript(
371  handshakeContext, authRequest, encodedEmptyCertMsg);
372  auto emptyAuthTranscriptHash =
373  detail::computeTranscriptHash(kderiver, emptyAuthTranscript);
374  auto finVerify = detail::getFinishedData(
375  kderiver, finishedMacKey, emptyAuthTranscriptHash);
376  Finished emptyAuth;
377  emptyAuth.verify_data = std::move(finVerify);
378  auto encodedEmptyAuth = encodeHandshake(std::move(emptyAuth));
379  return encodedEmptyAuth;
380 }
381 
382 } // namespace detail
383 } // namespace fizz
virtual std::vector< SignatureScheme > getSupportedSigSchemes() const =0
Buf encodeHandshake(T &&handshakeMsg)
Definition: Types-inl.h:515
void verify(int extras)
virtual CertificateMsg getCertMessage(Buf certificateRequestContext=nullptr) const =0
static std::unique_ptr< IOBuf > create(std::size_t capacity)
Definition: IOBuf.cpp:229
Buf encode< CertificateRequest >(CertificateRequest &&cr)
Definition: Types-inl.h:384
context
Definition: CMakeCache.txt:563
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
void push(const uint8_t *buf, size_t len)
Definition: Cursor.h:755
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
Range< unsigned char * > MutableByteRange
Definition: Range.h:1164
static const std::string encodedCertVerify
Buf certificate_request_context
Definition: Types.h:238
tuple make_tuple()
Definition: gtest-tuple.h:675
CipherSuite cipher
Buf certificate_request_context
Definition: Types.h:253
int current
void writeBuf(const Buf &buf, folly::io::Appender &out)
static folly::Optional< Param > decodeHandshakeMessage(folly::IOBufQueue &buf)
Definition: RecordLayer.cpp:93
static Options cacheChainLength()
Definition: IOBufQueue.h:83
std::vector< Extension > extensions
Definition: Types.h:254
constexpr auto data(C &c) -> decltype(c.data())
Definition: Access.h:71
virtual Buf getEkm(folly::StringPiece label, const Buf &context, uint16_t length) const =0
Buf computeTranscript(const Buf &handshakeContext, const Buf &authenticatorRequest, const Buf &certificate)
constexpr detail::Sig< Sig > const sig
Definition: Poly.h:1165
Definition: Actions.h:16
folly::Optional< std::vector< SignatureScheme > > getRequestedSchemes(const std::vector< fizz::Extension > &authRequestExtensions)
folly::Optional< SignatureScheme > getSignatureScheme(const std::vector< SignatureScheme > &supportedSchemes, const SelfCert &cert, const std::vector< fizz::Extension > &authRequestExtensions)
Buf getEmptyAuthenticator(std::unique_ptr< KeyDerivation > &kderiver, Buf authRequest, Buf handshakeContext, Buf finishedMacKey)
virtual Buf sign(SignatureScheme scheme, CertificateVerifyContext context, folly::ByteRange toBeSigned) const =0
Buf getFinishedData(std::unique_ptr< KeyDerivation > &deriver, Buf &finishedMacKey, const Buf &finishedTranscript)
Buf verify_data
Definition: Types.h:278
CertificateVerifyContext
Definition: Certificate.h:20
SignatureScheme algorithm
Definition: Types.h:273
std::unique_ptr< folly::IOBuf > Buf
Definition: Types.h:22
std::tuple< Buf, std::vector< fizz::Extension > > decodeAuthRequest(const Buf &authRequest)
Buf computeTranscriptHash(std::unique_ptr< KeyDerivation > &deriver, const Buf &toBeHashed)
static std::unique_ptr< IOBuf > copyBuffer(const void *buf, std::size_t size, std::size_t headroom=0, std::size_t minTailroom=0)
Definition: IOBuf.h:1587
virtual folly::Optional< CipherSuite > getCipher() const =0
virtual std::unique_ptr< KeyDerivation > makeKeyDeriver(CipherSuite cipher) const
Definition: Factory.h:62
Buf computeFinishedTranscript(const Buf &crTranscript, const Buf &certVerify)
constexpr None none
Definition: Optional.h:87
virtual std::vector< SignatureScheme > getSigSchemes() const =0