/*
 *  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/Serialization.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 genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-256
 *  Output: Randomly generated ECDSA-openssl::P256 private key
 */
StringPiece kP256DelegatedCredKey = R"(-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgol1GyLd1oigpV72G
LgiGEcQZsSnVse6Ogdc21OXfS0KhRANCAAQq/x89z+FhnpwdSYyWTc8yT2BI8jlK
EYl+Yi5t17Rx6L3HuTPY/vpDOpCcQZ6gLgsHOqqsFV7zcG9iX7ADJo2I
-----END PRIVATE KEY-----
)";

StringPiece kP256DelegatedCredKeyWServerLabel =
    R"(-----BEGIN SERVER DC PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgol1GyLd1oigpV72G
LgiGEcQZsSnVse6Ogdc21OXfS0KhRANCAAQq/x89z+FhnpwdSYyWTc8yT2BI8jlK
EYl+Yi5t17Rx6L3HuTPY/vpDOpCcQZ6gLgsHOqqsFV7zcG9iX7ADJo2I
-----END SERVER DC PRIVATE KEY-----
)";

StringPiece kP256DelegatedCredKeyWClientLabel =
    R"(-----BEGIN CLIENT DC PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgol1GyLd1oigpV72G
LgiGEcQZsSnVse6Ogdc21OXfS0KhRANCAAQq/x89z+FhnpwdSYyWTc8yT2BI8jlK
EYl+Yi5t17Rx6L3HuTPY/vpDOpCcQZ6gLgsHOqqsFV7zcG9iX7ADJo2I
-----END CLIENT DC 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 kP256ServerDelegatedCredNoLabel = {
    "CRlGSAQDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQ"
    "gAEKv8fPc/hYZ6cHUmMlk3PMk9gSPI5ShGJfmIubde0cei"
    "9x7kz2P76QzqQnEGeoC4LBzqqrBVe83BvYl+wAyaNiAQDA"
    "EYwRAIgMAKLN6Tzp4EHPcFE/fJaT4hPFrdGaOa3Gouw/zo"
    "sgkYCIHLScqwdF8N3c8LsksL4rE485wUOnhZ0+4aZ15W/qo"
    "jw"};

StringPiece kP256ClientDelegatedCredNoLabel = {
    "CRlGSAQDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQ"
    "gAEKv8fPc/hYZ6cHUmMlk3PMk9gSPI5ShGJfmIubde0cei"
    "9x7kz2P76QzqQnEGeoC4LBzqqrBVe83BvYl+wAyaNiAQDA"
    "EYwRAIgSNvyeG0plZlU79MnilfpqpOIdeXLNSmUttM2/19"
    "uI6cCIGFjtqRON2paWMAluGcogcNmO0Vi0uHIRwGzu6Y2Af"
    "aK"};

StringPiece kP256ServerDelegatedCred = {
    "-----BEGIN SERVER DELEGATED CREDENTIAL-----\n"
    "CRlGSAQDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQ"
    "gAEKv8fPc/hYZ6cHUmMlk3PMk9gSPI5ShGJfmIubde0cei"
    "9x7kz2P76QzqQnEGeoC4LBzqqrBVe83BvYl+wAyaNiAQDA"
    "EYwRAIgMAKLN6Tzp4EHPcFE/fJaT4hPFrdGaOa3Gouw/zo"
    "sgkYCIHLScqwdF8N3c8LsksL4rE485wUOnhZ0+4aZ15W/qo"
    "jw\n"
    "-----END SERVER DELEGATED CREDENTIAL-----\n"};

StringPiece kP256ClientDelegatedCred = {
    "-----BEGIN CLIENT DELEGATED CREDENTIAL-----\n"
    "CRlGSAQDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQ"
    "gAEKv8fPc/hYZ6cHUmMlk3PMk9gSPI5ShGJfmIubde0cei"
    "9x7kz2P76QzqQnEGeoC4LBzqqrBVe83BvYl+wAyaNiAQDA"
    "EYwRAIgSNvyeG0plZlU79MnilfpqpOIdeXLNSmUttM2/19"
    "uI6cCIGFjtqRON2paWMAluGcogcNmO0Vi0uHIRwGzu6Y2Af"
    "aK\n"
    "-----END CLIENT DELEGATED CREDENTIAL-----\n"};

StringPiece kP256ServerDelegatedCredBadLabel = {
    "-----BEGIN SERVERS DELEGATED CREDENTIAL-----\n"
    "CRlGSAQDAABbMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQ"
    "gAEKv8fPc/hYZ6cHUmMlk3PMk9gSPI5ShGJfmIubde0cei"
    "9x7kz2P76QzqQnEGeoC4LBzqqrBVe83BvYl+wAyaNiAQDA"
    "EYwRAIgMAKLN6Tzp4EHPcFE/fJaT4hPFrdGaOa3Gouw/zo"
    "sgkYCIHLScqwdF8N3c8LsksL4rE485wUOnhZ0+4aZ15W/qo"
    "jw\n"
    "-----END SERVERS DELEGATED CREDENTIAL-----\n"};

StringPiece kP384ServerDelegatedKey = {
    "-----BEGIN SERVER DC PRIVATE KEY-----\n"
    "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCdidyCCBy328Nk0ZXT"
    "f0kfeUpcGql3NsW8LAKeW4IyKoqd1iXQMrMbqKqslMjGnBWhZANiAAS24ZlJC1ZK"
    "86NYdg1C9eC8FtPw+1Oo/0H49PdYRC3kzlLoBdZFuvY6/LVHDILpC9/gaJsUOYTo"
    "MpJRsnZr96RMvPJuhjHGdqu6rhPI298VbEwtc7lIMMrY5E76dGk0gso=\n"
    "-----END SERVER DC PRIVATE KEY-----\n"};

StringPiece kP384ServerDelegatedCred = {
    "-----BEGIN SERVER DELEGATED CREDENTIAL-----\n"
    "CRlJ6QUDAAB4MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEtu"
    "GZSQtWSvOjWHYNQvXgvBbT8PtTqP9B+PT3WEQt5M5S6AXWRbr2Ovy1RwyC6Qvf4GibFDmE6DKSUbJ2a/ekTLzyboYxxnaruq4TyNvfFWxMLXO5SDDK2ORO+nRpNILKBAMASDBGAiEAj4Gr1NUAvGmJK4L9+q24HSfb9TJi2wXvF/AQMmB70a0CIQChfMy9fi0uiS9aUlvMRArpz/P0yCDNn83S9ueI/1N1uw==\n"
    "-----END SERVER DELEGATED CREDENTIAL-----\n"};

TEST(SerializationTest, testValidReadFromPem) {
  auto combinedPem = kP256ServerDelegatedCred.toString() +
      kP256DelegatedCredKeyWServerLabel.toString() + kP256CredCert.toString();
  auto dc = loadDCFromPEM(combinedPem, DelegatedCredentialMode::Server);
  EXPECT_NE(dc, nullptr);
  EXPECT_EQ(
      dc->getSigSchemes(),
      std::vector<SignatureScheme>{SignatureScheme::ecdsa_secp256r1_sha256});
}

TEST(SerializationTest, testReadFromPemWrongMode) {
  auto combinedPem = kP256ServerDelegatedCred.toString() +
      kP256DelegatedCredKeyWServerLabel.toString() + kP256CredCert.toString();
  EXPECT_THROW(
      loadDCFromPEM(combinedPem, DelegatedCredentialMode::Client),
      std::runtime_error);
}

TEST(SerializationTest, testReadClientAndServer) {
  auto combinedPem = kP256ClientDelegatedCred.toString() +
      kP256DelegatedCredKeyWClientLabel.toString() +
      kP256ServerDelegatedCred.toString() +
      kP256DelegatedCredKeyWServerLabel.toString() + kP256CredCert.toString();
  auto clientDC = loadDCFromPEM(combinedPem, DelegatedCredentialMode::Client);
  auto serverDC = loadDCFromPEM(combinedPem, DelegatedCredentialMode::Server);
  EXPECT_EQ(
      clientDC->getSigSchemes(),
      std::vector<SignatureScheme>{SignatureScheme::ecdsa_secp256r1_sha256});
  EXPECT_EQ(
      serverDC->getSigSchemes(),
      std::vector<SignatureScheme>{SignatureScheme::ecdsa_secp256r1_sha256});
}

TEST(SerializationTest, BadLabel) {
  auto combinedPem = kP256ServerDelegatedCredBadLabel.toString() +
      kP256DelegatedCredKeyWServerLabel.toString() + kP256CredCert.toString();
  EXPECT_THROW(
      loadDCFromPEM(combinedPem, DelegatedCredentialMode::Server),
      std::runtime_error);
}

TEST(SerializationTest, P384DC) {
  auto combinedPem = kP384ServerDelegatedCred.toString() +
      kP384ServerDelegatedKey.toString() + kP256CredCert.toString();
  EXPECT_THROW(
      loadDCFromPEM(combinedPem, DelegatedCredentialMode::Server),
      std::runtime_error);
}

TEST(SerializationTest, TestBuildServerOnlyPEM) {
  auto credData =
      folly::base64Decode(kP256ServerDelegatedCredNoLabel.toString());
  std::vector<Extension> credVec;
  credVec.emplace_back(Extension{
      ExtensionType::delegated_credential,
      folly::IOBuf::copyBuffer(std::move(credData))});
  auto serverCred = getExtension<DelegatedCredential>(std::move(credVec));
  auto combinedPem = kP256ServerDelegatedCred.toString() +
      kP256DelegatedCredKeyWServerLabel.toString();

  EXPECT_EQ(
      combinedPem,
      generateDelegatedCredentialPEM(
          DelegatedCredentialMode::Server,
          std::move(*serverCred),
          kP256DelegatedCredKey.toString()));
}

TEST(SerializationTest, TestBuildClientOnlyPEM) {
  auto credData =
      folly::base64Decode(kP256ClientDelegatedCredNoLabel.toString());
  std::vector<Extension> credVec;
  credVec.emplace_back(Extension{
      ExtensionType::delegated_credential,
      folly::IOBuf::copyBuffer(std::move(credData))});
  auto clientCred = getExtension<DelegatedCredential>(std::move(credVec));
  auto combinedPem = kP256ClientDelegatedCred.toString() +
      kP256DelegatedCredKeyWClientLabel.toString();

  EXPECT_EQ(
      combinedPem,
      generateDelegatedCredentialPEM(
          DelegatedCredentialMode::Client,
          std::move(*clientCred),
          kP256DelegatedCredKey.toString()));
}

TEST(SerializationTest, TestBuildMismatchedPEM) {
  auto credData =
      folly::base64Decode(kP256ClientDelegatedCredNoLabel.toString());
  std::vector<Extension> credVec;
  credVec.emplace_back(Extension{
      ExtensionType::delegated_credential,
      folly::IOBuf::copyBuffer(std::move(credData))});
  auto clientCred = getExtension<DelegatedCredential>(std::move(credVec));
  auto combinedPem = kP256ClientDelegatedCred.toString() +
      kP256DelegatedCredKeyWClientLabel.toString();

  EXPECT_NE(
      combinedPem,
      generateDelegatedCredentialPEM(
          DelegatedCredentialMode::Server,
          std::move(*clientCred),
          kP256DelegatedCredKey.toString()));
}

TEST(SerializationTest, TestBuildCombinedClientAndServerPEM) {
  auto credData =
      folly::base64Decode(kP256ServerDelegatedCredNoLabel.toString());
  std::vector<Extension> credVec;
  credVec.emplace_back(Extension{
      ExtensionType::delegated_credential,
      folly::IOBuf::copyBuffer(std::move(credData))});
  auto serverCred = getExtension<DelegatedCredential>(std::move(credVec));
  auto credData2 =
      folly::base64Decode(kP256ClientDelegatedCredNoLabel.toString());
  std::vector<Extension> credVec2;
  credVec2.emplace_back(Extension{
      ExtensionType::delegated_credential,
      folly::IOBuf::copyBuffer(std::move(credData2))});
  auto clientCred = getExtension<DelegatedCredential>(std::move(credVec2));

  auto combinedPem = kP256ClientDelegatedCred.toString() +
      kP256DelegatedCredKeyWClientLabel.toString() +
      kP256ServerDelegatedCred.toString() +
      kP256DelegatedCredKeyWServerLabel.toString() + kP256CredCert.toString();
  EXPECT_EQ(
      combinedPem,
      generateDelegatedCredentialPEM(
          DelegatedCredentialMode::Client,
          std::move(*clientCred),
          kP256DelegatedCredKey.toString()) +
          generateDelegatedCredentialPEM(
              DelegatedCredentialMode::Server,
              std::move(*serverCred),
              kP256DelegatedCredKey.toString()) +
          kP256CredCert.toString());
}
} // namespace test
} // namespace extensions
} // namespace fizz
