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 <fizz/extensions/delegatedcred/DelegatedCredentialUtils.h>
10 #include <fizz/tool/FizzCommandCommon.h>
11 #include <fizz/util/Parse.h>
12 #include <folly/FileUtil.h>
13
14 using namespace fizz::extensions;
15 using namespace folly;
16
17 namespace fizz {
18 namespace tool {
19 namespace {
20
printUsage()21 void printUsage() {
22 // clang-format off
23 std::cerr
24 << "Usage: gendc args\n"
25 << "\n"
26 << "Supported arguments:\n"
27 << " -cert cert (PEM format parent certificate. Required)\n"
28 << " -key key (PEM format private key for parent certificate. Required.)\n"
29 << " -pass password (certificate private key password. Default: none)\n"
30 << " -credkey key (PEM format public/private key for credential. Required.)\n"
31 << " -credpass password (credential private key password. Default: none)\n"
32 << " -signscheme scheme (signature scheme to sign the credential with. Default: first scheme supported by cert)\n"
33 << " -verifscheme scheme (signature scheme the credential can use. Default: first scheme supported by credential key)\n"
34 << " -validsec seconds (credential validity in seconds. Default: one day)\n"
35 << " -out path (file to output credential to. Default: none, prints to standard out)\n";
36 // clang-format on
37 }
38 } // namespace
39
fizzGenerateDelegatedCredentialCommand(const std::vector<std::string> & args)40 int fizzGenerateDelegatedCredentialCommand(
41 const std::vector<std::string>& args) {
42 // clang-format off
43 std::string certPath;
44 std::string certKeyPath;
45 std::string credKeyPath;
46 std::string certKeyPass;
47 std::string credKeyPass;
48 Optional<SignatureScheme> credSignScheme;
49 Optional<SignatureScheme> credVerifScheme;
50 std::chrono::seconds validSec{86400}; // 1 day
51 std::string outPath;
52
53 FizzArgHandlerMap handlers = {
54 {"-cert", {true, [&certPath](const std::string& arg) { certPath = arg; }}},
55 {"-key", {true, [&certKeyPath](const std::string& arg) { certKeyPath = arg; }}},
56 {"-pass", {true, [&certKeyPass](const std::string& arg) { certKeyPass = arg; }}},
57 {"-credkey", {true, [&credKeyPath](const std::string& arg) { credKeyPath = arg; }}},
58 {"-credpass", {true, [&credKeyPass](const std::string& arg) { credKeyPass = arg; }}},
59 {"-signscheme", {true, [&credSignScheme](const std::string& arg) {
60 credSignScheme = parse<SignatureScheme>(arg);
61 }}},
62 {"-verifscheme", {true, [&credVerifScheme](const std::string& arg) {
63 credVerifScheme = parse<SignatureScheme>(arg);
64 }}},
65 {"-validsec", {true, [&validSec](const std::string& arg) {
66 validSec = std::chrono::seconds(std::stoul(arg));
67 }}},
68 {"-out", {true, [&outPath](const std::string& arg) {
69 outPath = arg;
70 }}}
71 };
72 // clang-format on
73
74 try {
75 if (parseArguments(args, handlers, printUsage)) {
76 // Parsing failed, return
77 return 1;
78 }
79 } catch (const std::exception& e) {
80 LOG(ERROR) << "Error: " << e.what();
81 return 1;
82 }
83
84 if (certPath.empty()) {
85 LOG(ERROR) << "-cert is a required argument for gendc";
86 printUsage();
87 return 1;
88 }
89
90 if (certKeyPath.empty()) {
91 LOG(ERROR) << "-key is a required argument for gendc";
92 printUsage();
93 return 1;
94 }
95
96 if (credKeyPath.empty()) {
97 LOG(ERROR) << "-credkey is a required argument for gendc";
98 printUsage();
99 return 1;
100 }
101
102 std::string certData;
103 std::string certKeyData;
104 std::string credKeyData;
105 if (!readFile(certPath.c_str(), certData)) {
106 LOG(ERROR) << "Failed to read certificate";
107 return 1;
108 } else if (!readFile(certKeyPath.c_str(), certKeyData)) {
109 LOG(ERROR) << "Failed to read cert private key";
110 return 1;
111 } else if (!readFile(credKeyPath.c_str(), credKeyData)) {
112 LOG(ERROR) << "Failed to read credential private key";
113 return 1;
114 }
115
116 try {
117 std::shared_ptr<SelfCert> cert;
118 if (!certKeyPass.empty()) {
119 cert = CertUtils::makeSelfCert(certData, certKeyData, certKeyPass);
120 } else {
121 cert = CertUtils::makeSelfCert(certData, certKeyData);
122 }
123
124 folly::ssl::EvpPkeyUniquePtr certPrivKey =
125 CertUtils::readPrivateKeyFromBuffer(
126 certKeyData, certKeyPass.empty() ? nullptr : &certKeyPass[0]);
127
128 folly::ssl::EvpPkeyUniquePtr credPrivKey =
129 CertUtils::readPrivateKeyFromBuffer(
130 credKeyData, credKeyPass.empty() ? nullptr : &credKeyPass[0]);
131
132 if (!credSignScheme) {
133 credSignScheme = cert->getSigSchemes().front();
134 }
135
136 if (!credVerifScheme) {
137 switch (CertUtils::getKeyType(credPrivKey)) {
138 case KeyType::RSA:
139 credVerifScheme = CertUtils::getSigSchemes<KeyType::RSA>().front();
140 break;
141 case KeyType::P256:
142 credVerifScheme = CertUtils::getSigSchemes<KeyType::P256>().front();
143 break;
144 case KeyType::P384:
145 credVerifScheme = CertUtils::getSigSchemes<KeyType::P384>().front();
146 break;
147 case KeyType::P521:
148 credVerifScheme = CertUtils::getSigSchemes<KeyType::P521>().front();
149 break;
150 case KeyType::ED25519:
151 credVerifScheme =
152 CertUtils::getSigSchemes<KeyType::ED25519>().front();
153 break;
154 }
155 }
156
157 auto credential = DelegatedCredentialUtils::generateCredential(
158 std::move(cert),
159 certPrivKey,
160 credPrivKey,
161 *credSignScheme,
162 *credVerifScheme,
163 validSec);
164 auto encodedCred = fizz::extensions::encodeExtension(credential);
165 auto credData = encodedCred.extension_data->moveToFbString().toStdString();
166 if (outPath.empty()) {
167 std::cout << credData;
168 } else {
169 if (!writeFile(credData, outPath.c_str())) {
170 LOG(ERROR) << "Failed to write out credential: " << errnoStr(errno);
171 return 1;
172 }
173 }
174 } catch (const std::exception& e) {
175 LOG(ERROR) << "Failed to generate credential: " << e.what();
176 return 1;
177 }
178
179 return 0;
180 }
181
182 } // namespace tool
183 } // namespace fizz
184