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