Skip to content

Commit

Permalink
Add utility functions for reading/writing combined PEM
Browse files Browse the repository at this point in the history
Summary: Utility functions for reading/writing a combined Cert + Delegated Credential (and it's corresponding key)

Reviewed By: mingtaoy

Differential Revision: D55970428

fbshipit-source-id: 5dcd19d375fed7bdf95a0dfe23f207feeb68d9da
  • Loading branch information
Ajanthan Asogamoorthy authored and facebook-github-bot committed May 2, 2024
1 parent a1260a6 commit 19dca4c
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 4 deletions.
20 changes: 20 additions & 0 deletions fizz/extensions/delegatedcred/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,26 @@ cpp_library(
],
)

cpp_library(
name = "delegated_credential_pem_utils",
srcs = [
"DelegatedCredentialPemUtils.cpp",
],
headers = [
"DelegatedCredentialPemUtils.h",
],
deps = [
":delegated_credential_utils",
"//folly:base64",
"//folly:format",
"//folly:range",
],
exported_deps = [
":delegated_credential",
":self_delegated_credential",
],
)

cpp_library(
name = "peer_delegated_credential",
headers = [
Expand Down
67 changes: 67 additions & 0 deletions fizz/extensions/delegatedcred/DelegatedCredentialPemUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#include <fizz/extensions/delegatedcred/DelegatedCredentialPemUtils.h>
#include <fizz/extensions/delegatedcred/DelegatedCredentialUtils.h>
#include <folly/Format.h>
#include <folly/Range.h>
#include <folly/base64.h>

namespace fizz {
namespace extensions {

namespace {
static constexpr folly::StringPiece kDCHeader =
"-----BEGIN FIZZ DELEGATED CREDENTIAL-----\n";
static constexpr folly::StringPiece kDCFooter =
"-----END FIZZ DELEGATED CREDENTIAL-----\n";
} // namespace

std::string generateDelegatedCredentialPEM(
DelegatedCredential credential,
std::string certData,
std::string credKeyData) {
auto encodedCred = fizz::extensions::encodeExtension(credential);
std::string pemData;
pemData += kDCHeader;
pemData += folly::base64Encode(
encodedCred.extension_data->moveToFbString().toStdString());
pemData += "\n";
pemData += kDCFooter;
pemData += credKeyData;
pemData += certData;
return pemData;
}

std::unique_ptr<SelfDelegatedCredential> loadDCFromPEM(
std::string combinedPemData) {
auto certs = folly::ssl::OpenSSLCertUtils::readCertsFromBuffer(
folly::StringPiece(combinedPemData));
auto privKey = fizz::CertUtils::readPrivateKeyFromBuffer(combinedPemData);
auto credDataPtr = combinedPemData.find(kDCHeader);
auto credDataEndPtr = combinedPemData.find(kDCFooter);
if (!(credDataPtr != std::string::npos &&
credDataEndPtr != std::string::npos)) {
throw std::runtime_error(folly::sformat(
"Failed to load delegated credential from pem, expected label {} which was not found",
kDCHeader));
}
folly::Optional<DelegatedCredential> cred;
try {
auto credData = folly::base64Decode(combinedPemData.substr(
credDataPtr + kDCHeader.size(),
credDataEndPtr - credDataPtr - kDCHeader.size() - 1));

std::vector<Extension> credVec;
credVec.emplace_back(Extension{
ExtensionType::delegated_credential,
folly::IOBuf::copyBuffer(std::move(credData))});
cred = getExtension<DelegatedCredential>(std::move(credVec));
} catch (const std::exception& e) {
throw std::runtime_error(folly::sformat(
"Failed to decode delegated credential with exception {}", e.what()));
}
// Note we currently only support P256 this will throw if there is a mismatch
// in the delegated creds expected verification aglorithm
return std::make_unique<SelfDelegatedCredentialImpl<KeyType::P256>>(
std::move(certs), std::move(privKey), std::move(*cred));
}
} // namespace extensions
} // namespace fizz
29 changes: 29 additions & 0 deletions fizz/extensions/delegatedcred/DelegatedCredentialPemUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <fizz/extensions/delegatedcred/SelfDelegatedCredential.h>
#include <fizz/extensions/delegatedcred/Types.h>

#pragma once

namespace fizz {
namespace extensions {
/*
* We expect certData and credKeyData to already be in PEM format with their
* associated labels. We simply append the cert and key data so we expect these
* to be non combined pems. This returns one combined pem
*/
std::string generateDelegatedCredentialPEM(
DelegatedCredential credential,
std::string certData,
std::string credKeyData);
/*
* Takes in a string which we expect to be the Combined PEM including
* the leaf cert, the credential private key and the credential itself.
* Will return a cert that has the credential in its extensions.
* This currently only supports P256 Delegated credentials.
* If we use a different signature algorithm or the pem does not contain
* a valid delegated credentail this will throw, otherwise this will return
* a valid non null ptr;
*/
std::unique_ptr<SelfDelegatedCredential> loadDCFromPEM(
std::string combinedPemData);
} // namespace extensions
} // namespace fizz
13 changes: 13 additions & 0 deletions fizz/extensions/delegatedcred/test/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@ cpp_unittest(
],
)

cpp_unittest(
name = "delegated_credential_pem_utils_test",
srcs = [
"DelegatedCredentialPemUtilsTest.cpp",
],
deps = [
"//fizz/extensions/delegatedcred:delegated_credential_pem_utils",
"//folly:base64",
"//folly/portability:gmock",
"//folly/portability:gtest",
],
)

cpp_unittest(
name = "delegated_credential_utils_test",
srcs = [
Expand Down
160 changes: 160 additions & 0 deletions fizz/extensions/delegatedcred/test/DelegatedCredentialPemUtilsTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/*
* Copyright (c) 2019-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <fizz/extensions/delegatedcred/DelegatedCredentialPemUtils.h>
#include <folly/base64.h>
#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>

using namespace folly;

using namespace testing;

namespace fizz {
namespace extensions {
namespace test {

// @lint-ignore-every PRIVATEKEY

// clang-format off
/*
* Delegated credential certificate generated by kP256CredCertKey
* Prerequisites:
* - P133567922 in config.cfg
* - kP256CredCertKey in p256_key.pem
* Command: openssl req -new -key p256_key.pem -x509 -nodes -days {days} -config config.cfg
* Current cert set to expire in 2119.
* Output: Self-signed delegation certificate
*/
// clang-format on
StringPiece kP256CredCert = R"(
-----BEGIN CERTIFICATE-----
MIICKzCCAdGgAwIBAgIJAPi2vMRfOVd0MAoGCCqGSM49BAMCMGIxCzAJBgNVBAYT
AlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0RlZmF1bHQgQ29t
cGFueSBMdGQxHjAcBgNVBAMMFXJldnByb3h5LWRlbGVnYXRlZC1lYzAgFw0xOTA5
MjMwMjAyMzVaGA8yMTE5MDgzMDAyMDIzNVowYjELMAkGA1UEBhMCWFgxFTATBgNV
BAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UECgwTRGVmYXVsdCBDb21wYW55IEx0ZDEe
MBwGA1UEAwwVcmV2cHJveHktZGVsZWdhdGVkLWVjMFkwEwYHKoZIzj0CAQYIKoZI
zj0DAQcDQgAE7EbZMKds65EYciaSULFH4wZKt/OThiUL4uQW9cybr2HIzK68corO
JCeHXOsV3lpYS46b39SBZr1GZprFHH5gHaNuMGwwHQYDVR0OBBYEFMLkRMB4SclK
8K8uYMQBaYw0gNP7MB8GA1UdIwQYMBaAFMLkRMB4SclK8K8uYMQBaYw0gNP7MAwG
A1UdEwQFMAMBAf8wCwYDVR0PBAQDAgHmMA8GCSsGAQQBgtpLLAQCBQAwCgYIKoZI
zj0EAwIDSAAwRQIgB2EWbwWohYziQ2LmY8Qmn8y0WKR6Mbm5aad0rUBvtK4CIQCv
0U6Z/gFrVr0Cb2kc7M37KD9z5eeTwkQuGqs5GXF8Ow==
-----END CERTIFICATE-----
)";

/*
* Randomly generated ECDSA-openssl::P256 private key
* Command: openssl ecparam -name secp256r1 -genkey
* Output: Randomly generated ECDSA-openssl::P256 private key
*/
StringPiece kP256DelegatedCredKey = R"(
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIA8/keRkilh8bwUPxH9jiP5SsP4QiZtfofayTsRSI59poAoGCCqGSM49
AwEHoUQDQgAE8mV/wDAabnJbPLuF/qd/FMIWHDlrJI97cwq4obtPHyKFF2ukoG+6
/pXOUrEbsIH+/QBpZsnRHjvxryib97Ay+Q==
-----END EC PRIVATE KEY-----
)";

// clang-format off
/*
* Delegated credential generated using kP256CredCert, kP256CredCertKey & kP256DelegatedCredKey
* Prerequisites:
* - kP256CredCert in cert.pem
* - kP256CredCertKey in p256_key.pem
* - kP256DelegatedCredKey in p256_dc_key.pem
* Command: buck run //fizz/tool:fizz -- gendc -cert cert.pem -key p256_key.pem -credkey p256_dc_key.pem | xxd -p
* Output: Hex-encoded delegated credential
*/
// clang-format on

StringPiece kP256DelegatedCredNoLabel = {
"CI61NAQDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0"
"DAQcDQgAE8mV/wDAabnJbPLuF/qd/FMIWHDlrJI"
"97cwq4obtPHyKFF2ukoG+6/pXOUrEbsIH+/QBpZ"
"snRHjvxryib97Ay+QQDAEcwRQIgUrbmZ9uq07B0"
"Myg06hZj0uUq2f04eI4Ci0VIRP7XGhACIQDqckz"
"1AUqrX1gEw5DVBK8WQ3Q6pYSF4WI4gxIpvcCMEQ=="};

StringPiece kP256DelegatedCred = {
"-----BEGIN FIZZ DELEGATED CREDENTIAL-----\n"
"CI61NAQDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0"
"DAQcDQgAE8mV/wDAabnJbPLuF/qd/FMIWHDlrJI"
"97cwq4obtPHyKFF2ukoG+6/pXOUrEbsIH+/QBpZ"
"snRHjvxryib97Ay+QQDAEcwRQIgUrbmZ9uq07B0"
"Myg06hZj0uUq2f04eI4Ci0VIRP7XGhACIQDqckz"
"1AUqrX1gEw5DVBK8WQ3Q6pYSF4WI4gxIpvcCMEQ==\n"
"-----END FIZZ DELEGATED CREDENTIAL-----\n"};

StringPiece kP256DelegatedCredBadLabel = {
"-----BEGIN DC-----\n"
"CI61NAQDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0"
"DAQcDQgAE8mV/wDAabnJbPLuF/qd/FMIWHDlrJI"
"97cwq4obtPHyKFF2ukoG+6/pXOUrEbsIH+/QBpZ"
"snRHjvxryib97Ay+QQDAEcwRQIgUrbmZ9uq07B0"
"Myg06hZj0uUq2f04eI4Ci0VIRP7XGhACIQDqckz"
"1AUqrX1gEw5DVBK8WQ3Q6pYSF4WI4gxIpvcCMEQ==\n"
"-----END DC-----\n"};

StringPiece kP384DelegatedCred = {
"-----BEGIN FIZZ DELEGATED CREDENTIAL-----\n"
"CKpfvQUDAAB4MHYwEAYHKoZIzj0CAQYFK4EEACID"
"YgAEfDkvSAjmCrrxRNEkpA52igKI5pnE9q/MiKHTJ"
"/gfmJfhwK3IGI21+zs4DHvR2iowHSa5Ul+J5Fai9o"
"Nr32wTLUgQyrygUfhwNMuS4ucnd3Lz/EEJUBdkbEL"
"NnfY98HJcBAMARjBEAiBmz7fIOTsAXTQG55zdTxBy"
"QiUv7Ub3qtyYnJMaJKmx9gIgQiv40UZcP8GZkvDfg"
"D/EqLaFsDaP4Fl2PGH9NgXkyo8=\n"
"-----END FIZZ DELEGATED CREDENTIAL-----\n"};

TEST(PemUtilsTest, testValidReadFromPem) {
auto combinedPem = kP256DelegatedCred.toString() +
kP256DelegatedCredKey.toString() + kP256CredCert.toString();
auto dc = loadDCFromPEM(combinedPem);
EXPECT_NE(dc, nullptr);
EXPECT_EQ(
dc->getSigSchemes(),
std::vector<SignatureScheme>{SignatureScheme::ecdsa_secp256r1_sha256});
}

TEST(PemUtilsTest, BadLabel) {
auto combinedPem = kP256DelegatedCredBadLabel.toString() +
kP256DelegatedCredKey.toString() + kP256CredCert.toString();
EXPECT_THROW(loadDCFromPEM(combinedPem), std::runtime_error);
}

TEST(PemUtilsTest, P384DC) {
auto combinedPem = kP256DelegatedCredBadLabel.toString() +
kP256DelegatedCredKey.toString() + kP256CredCert.toString();
EXPECT_THROW(loadDCFromPEM(combinedPem), std::runtime_error);
}

TEST(PemUtilsTest, TestBuildCombinedPEM) {
auto credData = folly::base64Decode(kP256DelegatedCredNoLabel.toString());
std::vector<Extension> credVec;
credVec.emplace_back(Extension{
ExtensionType::delegated_credential,
folly::IOBuf::copyBuffer(std::move(credData))});
auto cred = getExtension<DelegatedCredential>(std::move(credVec));

auto combinedPem = kP256DelegatedCred.toString() +
kP256DelegatedCredKey.toString() + kP256CredCert.toString();
EXPECT_EQ(
combinedPem,
generateDelegatedCredentialPEM(
std::move(*cred),
kP256CredCert.toString(),
kP256DelegatedCredKey.toString()));
}
} // namespace test
} // namespace extensions
} // namespace fizz
1 change: 1 addition & 0 deletions fizz/tool/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ cpp_binary(
"//fizz/extensions/delegatedcred:delegated_credential_cert_manager",
"//fizz/extensions/delegatedcred:delegated_credential_client_extension",
"//fizz/extensions/delegatedcred:delegated_credential_factory",
"//fizz/extensions/delegatedcred:delegated_credential_pem_utils",
"//fizz/extensions/delegatedcred:delegated_credential_utils",
"//fizz/extensions/delegatedcred:self_delegated_credential",
"//fizz/protocol:certificate_verifier",
Expand Down
19 changes: 15 additions & 4 deletions fizz/tool/FizzGenerateDelegatedCredentialCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

#include <fizz/backend/openssl/certificate/CertUtils.h>
#include <fizz/extensions/delegatedcred/DelegatedCredentialPemUtils.h>
#include <fizz/extensions/delegatedcred/DelegatedCredentialUtils.h>
#include <fizz/tool/FizzCommandCommon.h>
#include <fizz/util/Parse.h>
Expand Down Expand Up @@ -165,19 +166,29 @@ int fizzGenerateDelegatedCredentialCommand(
}
}

folly::ssl::BioUniquePtr bio(BIO_new(BIO_s_mem()));
BUF_MEM* bptr = nullptr;
if (!PEM_write_bio_X509(bio.get(), cert->getX509().get())) {
throw std::runtime_error("Failed to re-encode cert");
}
BIO_get_mem_ptr(bio.get(), &bptr);
auto certPem = std::string(bptr->data, bptr->length);

auto credential = DelegatedCredentialUtils::generateCredential(
std::move(cert),
certPrivKey,
credPrivKey,
*credSignScheme,
*credVerifScheme,
validSec);
auto encodedCred = fizz::extensions::encodeExtension(credential);
auto credData = encodedCred.extension_data->moveToFbString().toStdString();

auto pem = fizz::extensions::generateDelegatedCredentialPEM(
std::move(credential), std::move(certPem), std::move(credKeyData));

if (outPath.empty()) {
std::cout << credData;
std::cout << pem;
} else {
if (!writeFile(credData, outPath.c_str())) {
if (!writeFile(pem, outPath.c_str())) {
LOG(ERROR) << "Failed to write out credential: " << errnoStr(errno);
return 1;
}
Expand Down

0 comments on commit 19dca4c

Please sign in to comment.