1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * This source code is licensed under the MIT license found in the
5  * LICENSE file in the root directory of this source tree.
6  *
7  */
8 
9 #include <quic/server/handshake/RetryTokenGenerator.h>
10 
11 #include <folly/Range.h>
12 #include <quic/codec/Decode.h>
13 
14 namespace {
15 
16 const std::vector<std::string> kCipherContexts = {"RetryToken"};
17 
18 } // namespace
19 
20 namespace quic {
21 
RetryTokenGenerator(RetryTokenSecret secret)22 RetryTokenGenerator::RetryTokenGenerator(RetryTokenSecret secret)
23     : cipher_(kCipherContexts) {
24   std::vector<folly::ByteRange> secrets;
25   secrets.emplace_back(folly::range(secret));
26   cipher_.setSecrets(secrets);
27 }
28 
encryptToken(const ConnectionId & connId,const folly::IPAddress & clientIp,uint16_t clientPort)29 folly::Optional<Buf> RetryTokenGenerator::encryptToken(
30     const ConnectionId& connId,
31     const folly::IPAddress& clientIp,
32     uint16_t clientPort) {
33   // Generate the retry token in plaintext
34   uint64_t timestampInMs =
35       std::chrono::duration_cast<std::chrono::milliseconds>(
36           std::chrono::system_clock::now().time_since_epoch())
37           .count();
38   RetryToken token(connId, clientIp, clientPort, timestampInMs);
39   auto plaintextToken = token.getPlaintextToken();
40 
41   // Try to encrypt it
42   auto maybeEncryptedToken = cipher_.encrypt(std::move(plaintextToken));
43   if (!maybeEncryptedToken) {
44     LOG(ERROR) << "Failed to encypt retry token with IP " << clientIp.str()
45                << " and port " << clientPort;
46   }
47 
48   // If the encryption failed, this will be empty optional
49   return maybeEncryptedToken;
50 }
51 
decryptToken(Buf encryptedToken)52 folly::Optional<RetryToken> RetryTokenGenerator::decryptToken(
53     Buf encryptedToken) {
54   auto maybeDecryptedToken = cipher_.decrypt(std::move(encryptedToken));
55   if (!maybeDecryptedToken) {
56     LOG(ERROR) << "Failed to decrypt token.";
57     return folly::none;
58   }
59 
60   // Try to parse the decrypted token
61   auto decryptedToken = (*maybeDecryptedToken).get();
62   folly::io::Cursor cursor(decryptedToken);
63   auto parseResult = parsePlaintextRetryToken(cursor);
64 
65   if (parseResult.hasError()) {
66     LOG(ERROR) << "Failed to parse decrypted retry token";
67     return folly::none;
68   }
69   return parseResult.value();
70 }
71 
72 } // namespace quic
73