/*
* Copyright (c) 2017, Intel Corporation
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:

* 1. Redistributions of source code must retain the above copyright notice,
*    this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <CertVerification/CertificateChain.h>

#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include <cstring>

static const std::string CORRECT_BEG_CERT = "-----BEGIN CERTIFICATE-----";
static const std::string CORRECT_END_CERT = "-----END CERTIFICATE-----";

static const std::string CORRECT_ROOT_CERT_BODY = R"cert(
MIICkDCCAjWgAwIBAgIVALf/CX<KEY>GSM49BAMC
MGgxGjAYBgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBD
b3Jwb3JhdGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQsw
CQYDVQQGEwJVUzAeFw0xODAzMjkxMDA3MTFaFw00OTEyMzEyMjU5NTlaMGgxGjAY
BgNVBAMMEUludGVsIFNHWCBSb290IENBMRowGAYDVQQKDBFJbnRlbCBDb3Jwb3Jh
dGlvbjEUMBIGA1UEBwwLU2FudGEgQ2xhcmExCzAJBgNVBAgMAkNBMQswCQYDVQQG
EwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOg+J8Dbw8l21h+KbjyFt8qH
9R6jRTx/t+EdBkgbknxB3rEVOW0xKIe68ILaGFQXzuc/V9KYULriAQ8EV84W7Ouj
gbswgbgwHwYDVR0jBBgwFoAUt/8JeCefSb2aR43u4uZ0Pw1WvmYwUgYDVR0fBEsw
STBHoEWgQ4ZBaHR0cHM6Ly9jZXJ0aWZpY2F0ZXMudHJ1c3RlZHNlcnZpY2VzLmlu
dGVsLmNvbS9JbnRlbFNHWFJvb3RDQS5jcmwwHQYDVR0OBBYEFLf/CXgnn0m9mkeN
7uLmdD8NVr5mMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMAoG
CCqGSM49BAMCA0kAMEYCIQDNrdFnV6RpzcxPeU4buq6XWJuVwWQJiYuhzAEtpn7p
bgIhAJbRv7dhz/yzCD14FmJuZnwI/3aDSvkc6MSD/K6m5ot0
)cert";
static const std::string CORRECT_INTERMEDIATE_CERT_BODY = R"cert(
<KEY>
)cert";
static const std::string CORRECT_PCK_CERT_BODY = R"cert(
<KEY>DBY<KEY>
<KEY>";

using namespace intel::sgx::qvl;
using namespace testing;

struct PckCertChainParserTests : public Test
{
    CertificateChain certChain;
    pckparser::CertStore rootCert, intermediateCert, finalCert;
};

TEST_F(PckCertChainParserTests, simpleParsing)
{
    // GIVEN
    const std::string txt = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;
    rootCert.parse(txt);
    
    // WHEN
    ASSERT_TRUE(certChain.parse(txt.c_str()));
   
    // THEN 
    EXPECT_EQ(1, certChain.length());
    EXPECT_EQ(*certChain.getRootCert(), rootCert);
    EXPECT_EQ(*certChain.getTopmostCert(), rootCert);
    EXPECT_EQ(*certChain.get(rootCert.getSubject()), rootCert);
}

TEST_F(PckCertChainParserTests, parsingTwoElements)
{
    // GIVEN
    const std::string txt1 = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;
    const std::string txt2 = CORRECT_BEG_CERT + CORRECT_INTERMEDIATE_CERT_BODY + CORRECT_END_CERT;
    const auto toParse = txt1 + txt2;
    rootCert.parse(txt1);
    intermediateCert.parse(txt2);

    // WHEN
    ASSERT_TRUE(certChain.parse(toParse.c_str()));

    // THEN
    EXPECT_EQ(2, certChain.length());
    EXPECT_EQ(*certChain.getRootCert(), rootCert);
    EXPECT_EQ(*certChain.getTopmostCert(), intermediateCert);
    EXPECT_EQ(*certChain.get(rootCert.getSubject()), rootCert);
    EXPECT_EQ(*certChain.get(intermediateCert.getSubject()), intermediateCert);

}

TEST_F(PckCertChainParserTests, parsingThreeElements)
{
    // GIVEN
    const std::string txt1 = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;
    const std::string txt2 = CORRECT_BEG_CERT + CORRECT_INTERMEDIATE_CERT_BODY + CORRECT_END_CERT;
    const std::string txt3 = CORRECT_BEG_CERT + CORRECT_PCK_CERT_BODY + CORRECT_END_CERT;
    const auto toParse = txt1 + txt2 + txt3;
    rootCert.parse(txt1);
    intermediateCert.parse(txt2);
    finalCert.parse(txt3);

    // WHEN
    ASSERT_TRUE(certChain.parse(toParse.c_str()));

    // THEN
    EXPECT_EQ(3, certChain.length());
    EXPECT_EQ(*certChain.getRootCert(), rootCert);
    EXPECT_EQ(*certChain.getTopmostCert(), finalCert);
    EXPECT_EQ(*certChain.get(rootCert.getSubject()), rootCert);
    EXPECT_EQ(*certChain.get(intermediateCert.getSubject()), intermediateCert);
    EXPECT_EQ(*certChain.get(finalCert.getSubject()), finalCert);
}

TEST_F(PckCertChainParserTests, parsingTwoElementsWithNewLines)
{
    // GIVEN
    const std::string txt1 = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;
    const std::string txt2 = CORRECT_BEG_CERT + CORRECT_INTERMEDIATE_CERT_BODY + CORRECT_END_CERT;
    // new lines at the end of txt1 and txt2 are expected to be gone
    const auto toParse = txt1 + "\n" + txt2 + "\n";

    // WHEN
    ASSERT_TRUE(certChain.parse(toParse.c_str()));

    // THEN
    EXPECT_EQ(2, certChain.length());
}

TEST_F(PckCertChainParserTests, parsingTwoElementsWithNewLinesWithMixedNewLinesTypes)
{
    // GIVEN
    const std::string txt1 = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;
    const std::string txt2 = CORRECT_BEG_CERT + CORRECT_INTERMEDIATE_CERT_BODY + CORRECT_END_CERT;
    // new lines at the end of txt1 and txt2 are expected to be gone
    const auto toParse = txt1 + "\r\n" + txt2 + "\n";

    // WHEN
    ASSERT_TRUE(certChain.parse(toParse.c_str()));

    // THEN
    EXPECT_EQ(2, certChain.length());
}

TEST_F(PckCertChainParserTests, parsingTwoElementsSecondIncorrectBegFormat)
{
    // GIVEN
    const std::string txt1 = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;
    // missing dash at the end of BE CERT
    const std::string txt2 = std::string("-----BEGIN CERTIFICATE----") + CORRECT_INTERMEDIATE_CERT_BODY + CORRECT_END_CERT;
    const auto toParse = txt1 + txt2;

    // WHEN
    ASSERT_TRUE(certChain.parse(toParse.c_str()));

    // THEN
    EXPECT_EQ(1, certChain.length());
}

TEST_F(PckCertChainParserTests, parsingTwoElementsMissingSecondBegFormat)
{
    // GIVEN
    const std::string txt1 = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;
    const std::string txt2 = CORRECT_INTERMEDIATE_CERT_BODY + CORRECT_END_CERT;
    const auto toParse = txt1 + txt2;

    // WHEN
    ASSERT_TRUE(certChain.parse(toParse.c_str()));

    // THEN
    EXPECT_EQ(1, certChain.length());
}

TEST_F(PckCertChainParserTests, parsingThreeElementsMissingSecondBegFormat)
{
    // GIVEN
    const std::string txt1 = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;
    const std::string txt2 = CORRECT_INTERMEDIATE_CERT_BODY + CORRECT_END_CERT;
    const std::string txt3 = CORRECT_BEG_CERT + CORRECT_PCK_CERT_BODY + CORRECT_END_CERT;

    const auto toParse = txt1 + txt2 + txt3;

    // WHEN
    ASSERT_TRUE(certChain.parse(toParse.c_str()));

    // THEN
    EXPECT_EQ(2, certChain.length());
}

TEST_F(PckCertChainParserTests, endCertificateHasMissingDashShouldReturnNothing)
{
    // GIVEN
    // END CERT has missing dash
    const std::string txt = CORRECT_BEG_CERT + CORRECT_ROOT_CERT_BODY + "-----END CERTIFICATE----";

    // WHEN
    ASSERT_FALSE(certChain.parse(txt.c_str()));

    // THEN
    EXPECT_EQ(0, certChain.length());
    EXPECT_EQ(certChain.getRootCert(), nullptr);
    EXPECT_EQ(certChain.getTopmostCert(), nullptr);
}

TEST_F(PckCertChainParserTests, begCertHasMissngDashShouldReturnNothing)
{
    // GIVEN
    // BEG CERT has missing dash
    const std::string txt = std::string("-----BEGIN CERTIFICATE----") + CORRECT_ROOT_CERT_BODY + CORRECT_END_CERT;

    // WHEN
    ASSERT_FALSE(certChain.parse(txt.c_str()));

    // THEN
    EXPECT_EQ(0, certChain.length());
    EXPECT_EQ(certChain.getRootCert(), nullptr);
    EXPECT_EQ(certChain.getTopmostCert(), nullptr);
}

TEST_F(PckCertChainParserTests, emptyString)
{
    // GIVEN

    // WHEN
    ASSERT_FALSE(certChain.parse(""));

    // THEN
    EXPECT_EQ(0, certChain.length());
    EXPECT_EQ(certChain.getRootCert(), nullptr);
    EXPECT_EQ(certChain.getTopmostCert(), nullptr);
}
