Add method to update certificate properities.

Read a certificate file and update properties to the D-Bus object

Change-Id: I108909a044dd678000142ea68cc019ab3d13f97b
Signed-off-by: Dhruvaraj Subhashchandran <dhruvaraj@in.ibm.com>
diff --git a/certificate.cpp b/certificate.cpp
index afd15ce..413ad54 100644
--- a/certificate.cpp
+++ b/certificate.cpp
@@ -21,6 +21,7 @@
     std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
 using X509_LOOKUP_Ptr =
     std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
+using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
 using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
 using BUF_MEM_Ptr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
 using InternalFailure =
@@ -37,6 +38,34 @@
      (errnum == X509_V_ERR_CERT_UNTRUSTED) ||                                  \
      (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
 
+// Refer to schema 2018.3
+// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
+// supported KeyUsage types in redfish
+// Refer to
+// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
+// key usage bit fields
+std::map<uint8_t, std::string> keyUsageToRfStr = {
+    {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
+    {KU_NON_REPUDIATION, "NonRepudiation"},
+    {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
+    {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
+    {KU_KEY_AGREEMENT, "KeyAgreement"},
+    {KU_KEY_CERT_SIGN, "KeyCertSign"},
+    {KU_CRL_SIGN, "CRLSigning"},
+    {KU_ENCIPHER_ONLY, "EncipherOnly"},
+    {KU_DECIPHER_ONLY, "DecipherOnly"}};
+
+// Refer to schema 2018.3
+// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
+// supported Extended KeyUsage types in redfish
+std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
+    {NID_server_auth, "ServerAuthentication"},
+    {NID_client_auth, "ClientAuthentication"},
+    {NID_email_protect, "EmailProtection"},
+    {NID_OCSP_sign, "OCSPSigning"},
+    {NID_ad_timeStamping, "Timestamping"},
+    {NID_code_sign, "CodeSigning"}};
+
 Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
                          const CertificateType& type,
                          const UnitsToRestart& unit,
@@ -228,6 +257,86 @@
     {
         reloadOrReset(unitToRestart);
     }
+
+    // Parse the certificate file and populate properties
+    populateProperties();
+}
+
+void Certificate::populateProperties()
+{
+    X509_Ptr cert = std::move(loadCert(certInstallPath));
+    // Update properties if no error thrown
+    BIO_MEM_Ptr certBio(BIO_new(BIO_s_mem()), BIO_free);
+    PEM_write_bio_X509(certBio.get(), cert.get());
+    BUF_MEM_Ptr certBuf(BUF_MEM_new(), BUF_MEM_free);
+    BUF_MEM* buf = certBuf.get();
+    BIO_get_mem_ptr(certBio.get(), &buf);
+    std::string certStr(buf->data, buf->length);
+    CertificateIface::certificateString(certStr);
+
+    static const int maxKeySize = 4096;
+    char subBuffer[maxKeySize] = {0};
+    // This pointer cannot be freed independantly.
+    X509_NAME* sub = X509_get_subject_name(cert.get());
+    X509_NAME_print_ex(certBio.get(), sub, 0, 0);
+    BIO_read(certBio.get(), subBuffer, maxKeySize);
+    CertificateIface::subject(subBuffer);
+
+    // This pointer cannot be freed independantly.
+    char issuerBuffer[maxKeySize] = {0};
+    X509_NAME* issuer_name = X509_get_issuer_name(cert.get());
+    X509_NAME_print_ex(certBio.get(), issuer_name, 0, 0);
+    BIO_read(certBio.get(), issuerBuffer, maxKeySize);
+    CertificateIface::issuer(issuerBuffer);
+
+    std::vector<std::string> keyUsageList;
+    ASN1_BIT_STRING* usage;
+
+    // Go through each usage in the bit string and convert to
+    // corresponding string value
+    if ((usage = static_cast<ASN1_BIT_STRING*>(
+             X509_get_ext_d2i(cert.get(), NID_key_usage, NULL, NULL))))
+    {
+        for (auto i = 0; i < usage->length; ++i)
+        {
+            for (auto& x : keyUsageToRfStr)
+            {
+                if (x.first & usage->data[i])
+                {
+                    keyUsageList.push_back(x.second);
+                    break;
+                }
+            }
+        }
+    }
+
+    EXTENDED_KEY_USAGE* extUsage;
+    if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
+             X509_get_ext_d2i(cert.get(), NID_ext_key_usage, NULL, NULL))))
+    {
+        for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
+        {
+            keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
+                sk_ASN1_OBJECT_value(extUsage, i))]);
+        }
+    }
+    CertificateIface::keyUsage(keyUsageList);
+
+    int days = 0;
+    int secs = 0;
+
+    ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
+    // Set time to 12:00am GMT, Jan 1 1970
+    ASN1_TIME_set_string(epoch.get(), "700101120000Z");
+
+    static const int dayToSeconds = 24 * 60 * 60;
+    ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
+    ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
+    CertificateIface::validNotAfter((days * dayToSeconds) + secs);
+
+    ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
+    ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
+    CertificateIface::validNotBefore((days * dayToSeconds) + secs);
 }
 
 X509_Ptr Certificate::loadCert(const std::string& filePath)
diff --git a/certificate.hpp b/certificate.hpp
index fc48f03..878ae71 100644
--- a/certificate.hpp
+++ b/certificate.hpp
@@ -81,6 +81,11 @@
      */
     X509_Ptr loadCert(const std::string& filePath);
 
+    /** @brief Populate certificate properties by parsing certificate file
+     *  @return void
+     */
+    void populateProperties();
+
     /** @brief Public/Private key compare function.
      *         Comparing private key against certificate public key
      *         from input .pem file.