text: /*
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include "PkcsAuthInit.hpp"
  
  #include <cstdio>
  #include <string>
  #include <util/Log.hpp>
  
  #include <geode/CacheableBuiltins.hpp>
  #include <geode/ExceptionTypes.hpp>
  #include <geode/Properties.hpp>
  
  #include "SerializationRegistry.hpp"
  #include "security_export.h"
  
  namespace apache {
  namespace geode {
  namespace client {
  std::shared_ptr<CacheableString> convertBytesToString(const uint8_t* bytes,
                                                        int32_t length,
                                                        size_t maxLength) {
    if (bytes) {
      std::string str;
      size_t totalBytes = 0;
      char byteStr[20];
      for (int32_t index = 0; index < length; ++index) {
        int len = sprintf(byteStr, "%d ", bytes[index]);
        totalBytes += len;
        // no use going beyond maxLength since LOG* methods will truncate
        // in any case
        if (maxLength > 0 && totalBytes > maxLength) {
          break;
        }
        str.append(byteStr, len);
      }
      return CacheableString::create(str);
    }
    return CacheableString::create("");
  }
  
  extern "C" {
  SECURITY_EXPORT AuthInitialize* createPKCSAuthInitInstance() {
    return new PKCSAuthInitInternal();
  }
  
  uint8_t* createSignature(EVP_PKEY* key, X509* cert,
                           const unsigned char* inputBuffer,
                           uint32_t inputBufferLen, unsigned int* signatureLen) {
    if (!key || !cert || !inputBuffer) {
      return nullptr;
    }
    const ASN1_OBJECT* macobj;
    const X509_ALGOR* algorithm = nullptr;
    X509_ALGOR_get0(&macobj, nullptr, nullptr, algorithm);
    const EVP_MD* signatureDigest = EVP_get_digestbyobj(macobj);
    EVP_MD_CTX* signatureCtx = EVP_MD_CTX_new();
    auto signatureData =
        std::unique_ptr<uint8_t[]>(new uint8_t[EVP_PKEY_size(key)]);
    bool result =
        (EVP_SignInit_ex(signatureCtx, signatureDigest, nullptr) &&
         EVP_SignUpdate(signatureCtx, inputBuffer, inputBufferLen) &&
         EVP_SignFinal(signatureCtx, signatureData.get(), signatureLen, key));
    EVP_MD_CTX_free(signatureCtx);
    if (result) {
      return signatureData.release();
    }
    return nullptr;
  }
  
  bool readPKCSPublicPrivateKey(FILE* keyStoreFP, const char* keyStorePassword,
                                EVP_PKEY** outPrivateKey, X509** outCertificate) {
    PKCS12* p12;
  
    if (!keyStoreFP || !keyStorePassword || (keyStorePassword[0] == '\0')) {
      return (false);
    }
  
    p12 = d2i_PKCS12_fp(keyStoreFP, nullptr);
  
    if (!p12) {
      return (false);
    }
  
    if (!PKCS12_parse(p12, keyStorePassword, outPrivateKey, outCertificate,
                      nullptr)) {
      return (false);
    }
  
    PKCS12_free(p12);
  
    return (outPrivateKey && outCertificate);
  }
  
  bool openSSLInit() {
    OpenSSL_add_all_algorithms();
    ERR_load_crypto_strings();
  
    return true;
  }
  
  static bool s_initDone = openSSLInit();
  }
  // end of extern "C"
  std::shared_ptr<Properties> PKCSAuthInitInternal::getCredentials(
      const std::shared_ptr<Properties>& securityprops, const std::string&) {
    if (!s_initDone) {
      throw AuthenticationFailedException(
          "PKCSAuthInit::getCredentials: "
          "OpenSSL initialization failed.");
    }
    if (securityprops == nullptr || securityprops->getSize() <= 0) {
      throw AuthenticationRequiredException(
          "PKCSAuthInit::getCredentials: "
          "No security-* properties are set.");
    }
  
    auto keyStoreptr = securityprops->find(KEYSTORE_FILE_PATH1);
  
    const char* keyStorePath = keyStoreptr->value().c_str();
  
    if (!keyStorePath) {
      throw AuthenticationFailedException(
          "PKCSAuthInit::getCredentials: "
          "key-store file path property KEYSTORE_FILE_PATH not set.");
    }
  
    auto aliasptr = securityprops->find(KEYSTORE_ALIAS1);
  
    const char* alias = aliasptr->value().c_str();
  
    if (!alias) {
      throw AuthenticationFailedException(
          "PKCSAuthInit::getCredentials: "
          "key-store alias property KEYSTORE_ALIAS not set.");
    }
  
    auto keyStorePassptr = securityprops->find(KEYSTORE_PASSWORD1);
  
    const char* keyStorePass = keyStorePassptr->value().c_str();
  
    if (!keyStorePass) {
      throw AuthenticationFailedException(
          "PKCSAuthInit::getCredentials: "
          "key-store password property KEYSTORE_PASSWORD not set.");
    }
  
    FILE* keyStoreFP = fopen(keyStorePath, "r");
    if (!keyStoreFP) {
      char msg[1024];
      sprintf(msg, "PKCSAuthInit::getCredentials: Unable to open keystore %s",
              keyStorePath);
      throw AuthenticationFailedException(msg);
    }
  
    EVP_PKEY* privateKey = nullptr;
    X509* cert = nullptr;
  
    /* Read the Public and Private Key from keystore in file */
    if (!readPKCSPublicPrivateKey(keyStoreFP, keyStorePass, &privateKey, &cert)) {
      fclose(keyStoreFP);
      char msg[1024];
      sprintf(msg,
              "PKCSAuthInit::getCredentials: Unable to read PKCS "
              "public key from %s",
              keyStorePath);
      throw AuthenticationFailedException(msg);
    }
  
    fclose(keyStoreFP);
    unsigned int lengthEncryptedData = 0;
  
    auto signatureData = std::unique_ptr<uint8_t[]>(createSignature(
        privateKey, cert, reinterpret_cast<const unsigned char*>(alias),
        static_cast<uint32_t>(strlen(alias)), &lengthEncryptedData));
    EVP_PKEY_free(privateKey);
    X509_free(cert);
    if (!signatureData) {
      throw AuthenticationFailedException(
          "PKCSAuthInit::getCredentials: "
          "Unable to create signature");
    }
    std::shared_ptr<Cacheable> signatureValPtr;
    if (m_stringCredentials) {
      // convert signature bytes to base64
      signatureValPtr =
          convertBytesToString(signatureData.get(), lengthEncryptedData, 2048);
      LOGINFO(" Converting CREDS to STRING: %s",
              signatureValPtr->toString().c_str());
    } else {
      signatureValPtr = CacheableBytes::create(std::vector<int8_t>(
          signatureData.get(), signatureData.get() + lengthEncryptedData));
      LOGINFO(" Converting CREDS to BYTES: %s",
              signatureValPtr->toString().c_str());
    }
    auto credentials = Properties::create();
    credentials->insert(KEYSTORE_ALIAS1, alias);
    credentials->insert(CacheableString::create(SIGNATURE_DATA1),
                        signatureValPtr);
    return credentials;
  }
  }  // namespace client
  }  // namespace geode
  }  // namespace apache
  