diff --git a/.editorconfig b/.editorconfig index 95b9ce0..beffa30 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,19 +7,5 @@ charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true -[*.cc] -indent_style = space -indent_size = 2 -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.h] -indent_style = space -indent_size = 2 -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - [*.md] trim_trailing_whitespace = false diff --git a/binding.gyp b/binding.gyp index a743a0f..8f9529d 100644 --- a/binding.gyp +++ b/binding.gyp @@ -19,17 +19,8 @@ }, 'defines': [ 'NAPI_CPP_EXCEPTIONS', 'NODE_API_SWALLOW_UNTHROWABLE_EXCEPTIONS', 'NODE_ADDON_API_DISABLE_DEPRECATED', 'NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED' ], 'sources': [ - 'lib/libasherah.h', - 'src/asherah.h', 'src/asherah.cc', - 'src/asherah_async_worker.h', - 'src/asherah_async_worker.cc', 'src/logging.cc', - 'src/logging.h', - 'src/cobhan_napi_interop.h', - 'src/cobhan.h', - 'src/cobhan.cc', - 'src/hints.h' ], 'libraries': [ '../lib/libasherah.a' ] } diff --git a/package.json b/package.json index 68df165..5e303ca 100644 --- a/package.json +++ b/package.json @@ -25,16 +25,14 @@ "license": "MIT", "files": [ "binding.gyp", - "src/asherah.h", "src/asherah.cc", - "src/asherah_async_worker.h", - "src/asherah_async_worker.cc", + "src/cobhan_buffer_napi.h", + "src/cobhan_buffer.h", "src/hints.h", - "src/logging.h", "src/logging.cc", - "src/cobhan_napi_interop.h", - "src/cobhan.h", - "src/cobhan.cc", + "src/logging.h", + "src/napi_utils.h", + "src/scoped_allocate.h", "src/asherah.d.ts", "scripts/download-libraries.sh", "scripts/build.sh", diff --git a/scripts/download-libraries.sh b/scripts/download-libraries.sh index d303028..a2fe1c5 100755 --- a/scripts/download-libraries.sh +++ b/scripts/download-libraries.sh @@ -1,58 +1,239 @@ #!/bin/bash -echo "Downloading Asherah libraries" - -# shellcheck source=/dev/null -source .asherah-version - -mkdir -p lib -cd lib || exit 1 - -OS=$(uname) -MACHINE=$(uname -m) - -if [[ "${OS}" == 'Linux' ]]; then - if [[ ${MACHINE} == 'x86_64' ]]; then - echo "Linux x64" - ARCHIVE=libasherah-x64.a - HEADER=libasherah-x64-archive.h - SUMS=SHA256SUMS - elif [[ ${MACHINE} == 'aarch64' ]]; then - echo "Linux arm64" - ARCHIVE=libasherah-arm64.a - HEADER=libasherah-arm64-archive.h - SUMS=SHA256SUMS +set -e # Exit on any command failure + +# Global Constants +CHECK_INTERVAL_SECONDS=$((5 * 60)) # 5 minutes + +# Function to check if a specific file download is necessary +function check_download_required { + local file=$1 + local no_cache=$2 + local check_interval_seconds=$3 + local etag_file="${file}.etag" + + # If the file does not exist or the .etag file is missing, download is required + if [[ ! -f "$file" || ! -f "$etag_file" ]]; then + return 1 # true (download required) + fi + + # If no-cache is passed, we ignore the modified date of the .etag files + if [[ "$no_cache" == true ]]; then + return 1 # true (download required) + fi + + # Check if the .etag file is older than the interval + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS uses 'stat -f' + last_check=$(stat -f %m "$etag_file") else - echo "Unsupported CPU architecture" - exit 1 - fi -elif [[ ${OS} == 'Darwin' ]]; then - if [[ ${MACHINE} == 'x86_64' ]]; then - echo "MacOS x64" - ARCHIVE=libasherah-darwin-x64.a - HEADER=libasherah-darwin-x64-archive.h - SUMS=SHA256SUMS-darwin - elif [[ ${MACHINE} == 'arm64' ]]; then - echo "MacOS arm64" - ARCHIVE=libasherah-darwin-arm64.a - HEADER=libasherah-darwin-arm64-archive.h - SUMS=SHA256SUMS-darwin + # Linux uses 'stat -c' + last_check=$(stat -c %Y "$etag_file") + fi + #last_check=$(stat -c %Y "$etag_file") + current_time=$(date +%s) + elapsed_time=$((current_time - last_check)) + + if (( elapsed_time > check_interval_seconds )); then + return 1 # true (download required) + fi + + return 0 # false (download not required) +} + +# Function to download a file +function download_file { + local url=$1 + local file=$2 + local etag_file="${file}.etag" + + if ! curl -s -L --fail --etag-save "$etag_file" --etag-compare "$etag_file" -O "$url"; then + echo "Failed to download $url" >&2 + exit 1 + fi + + # Explicitly touch the etag file to update its modification time only if successful + touch "$etag_file" +} + +# Function to verify checksums +function verify_checksums { + local archive=$1 + local header=$2 + local sums=$3 + + # Determine the available SHA hashing utility + if command -v sha256sum &> /dev/null; then + sha_cmd="sha256sum" + elif command -v shasum &> /dev/null; then + sha_cmd="shasum -a 256" + else + echo "Error: Neither sha256sum nor shasum is available on this system." >&2 + exit 1 + fi + + # Filter the relevant checksums and verify they are not empty + checksums=$(grep -e "${archive}" -e "${header}" "${sums}") + if [[ -z "$checksums" ]]; then + echo "Error: No matching checksums found for ${archive} or ${header} in ${sums}." >&2 + exit 1 + fi + + echo "$checksums" > ./SHA256SUM # Return value + + if ! $sha_cmd -c ./SHA256SUM; then + echo 'SHA256 mismatch!' >&2 + rm -f ./*.a ./*.h + exit 1 + fi +} + +# Function to copy library and header files +function copy_files { + local archive=$1 + local header=$2 + + if [[ ! -f "${archive}" ]]; then + echo "Error: Source archive file '${archive}' does not exist." >&2 + exit 1 + fi + + if [[ ! -f "${header}" ]]; then + echo "Error: Source header file '${header}' does not exist." >&2 + exit 1 + fi + + if ! cp -f "${archive}" libasherah.a; then + echo "Error: Failed to copy archive file '${archive}' to 'libasherah.a'." >&2 + exit 1 + fi + + if ! cp -f "${header}" libasherah.h; then + echo "Error: Failed to copy header file '${header}' to 'libasherah.h'." >&2 + exit 1 + fi +} + +# Function to detect OS and CPU architecture +function detect_os_and_cpu { + OS=$(uname) + MACHINE=$(uname -m) + + #echo "Detected OS: ${OS}" + #echo "Detected CPU architecture: ${MACHINE}" + + if [[ "${OS}" == 'Linux' ]]; then + if [[ ${MACHINE} == 'x86_64' ]]; then + #echo "Using Asherah libraries for Linux x86_64" + ARCHIVE="libasherah-x64.a" + HEADER="libasherah-x64-archive.h" + SUMS="SHA256SUMS" + elif [[ ${MACHINE} == 'aarch64' ]]; then + #echo "Using Asherah libraries for Linux aarch64" + ARCHIVE="libasherah-arm64.a" + HEADER="libasherah-arm64-archive.h" + SUMS="SHA256SUMS" + else + #echo "Unsupported CPU architecture: ${MACHINE}" >&2 + exit 1 + fi + elif [[ "${OS}" == 'Darwin' ]]; then + if [[ ${MACHINE} == 'x86_64' ]]; then + #echo "Using Asherah libraries for MacOS x86_64" + ARCHIVE="libasherah-darwin-x64.a" + HEADER="libasherah-darwin-x64-archive.h" + SUMS="SHA256SUMS-darwin" + elif [[ ${MACHINE} == 'arm64' ]]; then + #echo "Using Asherah libraries for MacOS arm64" + ARCHIVE="libasherah-darwin-arm64.a" + HEADER="libasherah-darwin-arm64-archive.h" + SUMS="SHA256SUMS-darwin" + else + echo "Unsupported CPU architecture: ${MACHINE}" >&2 + exit 1 + fi else - echo "Unsupported CPU architecture" + echo "Unsupported operating system: ${OS}" >&2 exit 1 fi -else - echo "Unsupported operating system" - exit 1 -fi -curl -s -L --fail --etag-save "${ARCHIVE}.etag" --etag-compare "${ARCHIVE}.etag" -O --retry 999 --retry-max-time 0 "https://github.com/godaddy/asherah-cobhan/releases/download/${ASHERAH_VERSION}/${ARCHIVE}" || echo "Failed to download ${ASHERAH_VERSION}/${ARCHIVE}" + echo "${ARCHIVE}" "${HEADER}" "${SUMS}" # Return value +} + +# Parse script arguments +function parse_args { + local no_cache=false + while [[ $# -gt 0 ]]; do + case "$1" in + --no-cache) + no_cache=true + shift + ;; + *) + echo "Unknown parameter: $1" >&2 + exit 1 + ;; + esac + done + echo "${no_cache}" # Return value +} + +# Function to determine the interval message +function interval_message { + local interval=$1 + if (( interval % 60 == 0 )); then + echo "$((interval / 60)) minutes" # Return value + else + echo "$interval seconds" # Return value + fi +} + +# Main function +function main { + echo "Downloading Asherah libraries" + source .asherah-version + + # Parse arguments + local no_cache + no_cache=$(parse_args "$@") + + # Detect OS and CPU architecture + read -r archive header sums < <(detect_os_and_cpu) + echo "Archive: $archive" + echo "Header: $header" + echo "Sums: $sums" + echo "Version: $ASHERAH_VERSION" + + # Interpolate the URLs + url_prefix="https://github.com/godaddy/asherah-cobhan/releases/download/${ASHERAH_VERSION}" + file_names=("${archive}" "${header}" "${sums}") + file_urls=( + "${url_prefix}/${archive}" + "${url_prefix}/${header}" + "${url_prefix}/${sums}" + ) + + # Create the `lib` directory if it doesn't exist + mkdir -p lib + cd lib || exit 1 + + # Per-file touch and download logic + # Now, you can access the URLs using the index of the filenames + for i in "${!file_names[@]}"; do + if check_download_required "${file_names[$i]}" "$no_cache" "$CHECK_INTERVAL_SECONDS"; then + download_file "${file_urls[$i]}" "${file_names[$i]}" + else + interval_str=$(interval_message "$CHECK_INTERVAL_SECONDS") + echo "${file_names[$i]} is up to date (checked within the last ${interval_str})" + fi + done -curl -s -L --fail --etag-save "${HEADER}.etag" --etag-compare "${HEADER}.etag" -O --retry 999 --retry-max-time 0 "https://github.com/godaddy/asherah-cobhan/releases/download/${ASHERAH_VERSION}/${HEADER}" || echo "Failed to download ${ASHERAH_VERSION}/${HEADER}" + # Verify checksums and copy files + verify_checksums "${archive}" "${header}" "${sums}" + copy_files "${archive}" "${header}" -curl -s -L --fail --etag-save "${SUMS}.etag" --etag-compare "${SUMS}.etag" -O --retry 999 --retry-max-time 0 "https://github.com/godaddy/asherah-cobhan/releases/download/${ASHERAH_VERSION}/${SUMS}" || echo "Failed to download ${ASHERAH_VERSION}/${SUMS}" -grep -e "${ARCHIVE}" -e "${HEADER}" "${SUMS}" > ./SHA256SUM -shasum -a 256 -c ./SHA256SUM || (echo 'SHA256 mismatch!' ; rm -f ./*.a ./*.h ; exit 1) + echo "Asherah libraries downloaded successfully" +} -cp -f "${ARCHIVE}" libasherah.a -cp -f "${HEADER}" libasherah.h +# Execute the main function +main "$@" diff --git a/src/asherah.cc b/src/asherah.cc index d94f0a3..6b25683 100644 --- a/src/asherah.cc +++ b/src/asherah.cc @@ -1,786 +1,839 @@ #include "../lib/libasherah.h" +#include "asherah_async_worker.h" +#include "cobhan_buffer_napi.h" #include "hints.h" #include "logging.h" -#include "cobhan_buffer_napi.h" -#include "scoped_allocate.h" #include "napi_utils.h" +#include "scoped_allocate.h" +#include #include #include -#include static volatile std::atomic setup_state; class Asherah : public Napi::Addon { public: - Asherah(Napi::Env, Napi::Object exports) { - setup_state = 0; - DefineAddon( - exports, - { - InstanceMethod("setup", &Asherah::SetupAsherahSync), - InstanceMethod("setup_async", &Asherah::SetupAsherahAsync), - InstanceMethod("encrypt", &Asherah::EncryptSync), - InstanceMethod("encrypt_async", &Asherah::EncryptAsync), - //InstanceMethod("encrypt_string", &Asherah::EncryptStringSync), - // InstanceMethod("encrypt_string_async", - // &Asherah::EncryptStringAsync), - InstanceMethod("decrypt", &Asherah::DecryptSync), - InstanceMethod("decrypt_async", &Asherah::DecryptAsync), - //InstanceMethod("decrypt_string", &Asherah::DecryptStringSync), - // InstanceMethod("decrypt_string_async", - // &Asherah::DecryptStringAsync), - InstanceMethod("shutdown", &Asherah::ShutdownAsherahSync), - InstanceMethod("shutdown_async", &Asherah::ShutdownAsherahAsync), - InstanceMethod("set_max_stack_alloc_item_size", - &Asherah::SetMaxStackAllocItemSize), - InstanceMethod("set_safety_padding_overhead", - &Asherah::SetSafetyPaddingOverhead), - InstanceMethod("get_setup_status", &Asherah::GetSetupStatus), - InstanceMethod("set_log_hook", &Asherah::SetLogHook), - }); - } + Asherah(Napi::Env, Napi::Object exports) { + setup_state = 0; + DefineAddon( + exports, + { + InstanceMethod("setup", &Asherah::SetupAsherahSync), + InstanceMethod("setup_async", &Asherah::SetupAsherahAsync), + InstanceMethod("encrypt", &Asherah::EncryptSync), + InstanceMethod("encrypt_async", &Asherah::EncryptAsync), + InstanceMethod("encrypt_string", &Asherah::EncryptSync), + InstanceMethod("encrypt_string_async", &Asherah::EncryptAsync), + InstanceMethod("decrypt", &Asherah::DecryptSync), + InstanceMethod("decrypt_async", &Asherah::DecryptAsync), + InstanceMethod("decrypt_string", &Asherah::DecryptStringSync), + InstanceMethod("decrypt_string_async", + &Asherah::DecryptStringAsync), + InstanceMethod("shutdown", &Asherah::ShutdownAsherahSync), + InstanceMethod("shutdown_async", &Asherah::ShutdownAsherahAsync), + InstanceMethod("set_max_stack_alloc_item_size", + &Asherah::SetMaxStackAllocItemSize), + InstanceMethod("set_safety_padding_overhead", + &Asherah::SetSafetyPaddingOverhead), + InstanceMethod("get_setup_status", &Asherah::GetSetupStatus), + InstanceMethod("set_log_hook", &Asherah::SetLogHook), + }); + } private: - size_t est_intermediate_key_overhead = 0; - size_t maximum_stack_alloc_size = 2048; - - int32_t verbose_flag = 0; - Napi::FunctionReference log_hook; - Logger logger; - - void BeginSetupAsherah(const Napi::Env &env, const char *func_name, - const Napi::CallbackInfo &info, - Napi::String &config_string, size_t &product_id_length, - size_t &service_name_length) { - std::cerr << "BeginSetupAsherah called" << std::endl << std::flush; - logger.error_log(__func__, "BeginSetupAsherah called"); - if (unlikely(setup_state.load(std::memory_order_acquire) != 0)) { - std::cerr << "BeginSetupAsherah - setup called twice" << std::endl << std::flush; - logger.log_error_and_throw(func_name, "setup called twice"); - return; - } - - if (unlikely(info.Length() < 1)) { - std::cerr << "BeginSetupAsherah - Wrong number of arguments" << std::endl << std::flush; - logger.log_error_and_throw(func_name, "Wrong number of arguments"); - return; - } - - std::cerr << "BeginSetupAsherah before AsJsonObjectAndString" << std::endl << std::flush; - Napi::Object config_json; - NapiUtils::AsJsonObjectAndString(env, info[0], config_string, config_json); + size_t est_intermediate_key_overhead = 0; + size_t maximum_stack_alloc_size = 2048; + + int32_t verbose_flag = 0; + Napi::FunctionReference log_hook; + Logger logger; + + void BeginSetupAsherah(const Napi::Env &env, const char *func_name, + const Napi::CallbackInfo &info, + Napi::String &config_string, size_t &product_id_length, + size_t &service_name_length) { + std::cerr << "BeginSetupAsherah called" << std::endl << std::flush; + logger.error_log(__func__, "BeginSetupAsherah called"); + if (unlikely(setup_state.load(std::memory_order_acquire) != 0)) { + std::cerr << "BeginSetupAsherah - setup called twice" << std::endl + << std::flush; + logger.log_error_and_throw(func_name, "setup called twice"); + return; + } + + if (unlikely(info.Length() < 1)) { + std::cerr << "BeginSetupAsherah - Wrong number of arguments" << std::endl + << std::flush; + logger.log_error_and_throw(func_name, "Wrong number of arguments"); + return; + } + + std::cerr << "BeginSetupAsherah before AsJsonObjectAndString" << std::endl + << std::flush; + Napi::Object config_json; + NapiUtils::AsJsonObjectAndString(env, info[0], config_string, config_json); + + std::cerr << "BeginSetupAsherah before GetStringProperty(ProductID)" + << std::endl + << std::flush; + Napi::String product_id; + NapiUtils::GetStringProperty(config_json, "ProductID", product_id); + product_id_length = NapiUtils::GetUtf8StringLength(env, product_id); + + std::cerr << "BeginSetupAsherah before GetStringProperty(ServiceName)" + << std::endl + << std::flush; + Napi::String service_name; + NapiUtils::GetStringProperty(config_json, "ServiceName", service_name); + service_name_length = NapiUtils::GetUtf8StringLength(env, service_name); + + std::cerr << "BeginSetupAsherah before GetBooleanProperty(Verbose)" + << std::endl + << std::flush; + bool verbose; + NapiUtils::GetBooleanProperty(config_json, "Verbose", verbose, false); + verbose_flag = verbose; + + std::cerr << "BeginSetupAsherah finished" << std::endl << std::flush; + } - std::cerr << "BeginSetupAsherah before GetStringProperty(ProductID)" << std::endl << std::flush; - Napi::String product_id; - NapiUtils::GetStringProperty(config_json, "ProductID", product_id); - product_id_length = NapiUtils::GetUtf8StringLength(env, product_id); + void EndSetupAsherah(GoInt32 result, size_t product_id_length, + size_t service_name_length) { + if (unlikely(result < 0)) { + logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); + return; + } - std::cerr << "BeginSetupAsherah before GetStringProperty(ServiceName)" << std::endl << std::flush; - Napi::String service_name; - NapiUtils::GetStringProperty(config_json, "ServiceName", service_name); - service_name_length = NapiUtils::GetUtf8StringLength(env, service_name); + est_intermediate_key_overhead = product_id_length + service_name_length; - std::cerr << "BeginSetupAsherah before GetBooleanProperty(Verbose)" << std::endl << std::flush; - bool verbose; - NapiUtils::GetBooleanProperty(config_json, "Verbose", verbose, false); - verbose_flag = verbose; + std::cerr << "******************* Setting setup_state to 1" << std::endl + << std::flush; + auto old_setup_state = setup_state.exchange(1, std::memory_order_acq_rel); + if (unlikely(old_setup_state != 0)) { + logger.log_error_and_throw(__func__, "lost race to mark setup_state!"); + return; + } + } - std::cerr << "BeginSetupAsherah finished" << std::endl << std::flush; + void SetupAsherahSync(const Napi::CallbackInfo &info) { + try { + const Napi::Env &env = info.Env(); + + Napi::String config_string; + size_t product_id_length; + size_t service_name_length; + + std::cerr << "SetupAsherahSync called" << std::endl << std::flush; + logger.error_log(__func__, "SetupAsherahSync called"); + + BeginSetupAsherah(env, __func__, info, config_string, product_id_length, + service_name_length); + + char *config_cbuffer; + std::cerr << "Calculate allocation size by using StringToAllocationSize" + << std::endl + << std::flush; + size_t config_cbuffer_size = + CobhanBufferNapi::StringToAllocationSize(env, config_string); + if (unlikely(config_cbuffer_size == 0)) { + std::cerr << "Failed to calculate allocation size for config string" + << std::endl + << std::flush; + logger.error_log( + __func__, "Failed to calculate allocation size for config string"); + return; + } + std::cerr << "Allocate cbuffer using ALLOCATE_CBUFFER macro" << std::endl + << std::flush; + SCOPED_ALLOCATE_BUFFER(logger, config_cbuffer, config_cbuffer_size, + maximum_stack_alloc_size, __func__); + + std::cerr << "Create config CobhanBufferNapi object" << std::endl + << std::flush; + CobhanBufferNapi config(env, config_string, config_cbuffer, + config_cbuffer_size); + if (env.IsExceptionPending()) { + std::cerr << "Error creating the config CobhanBufferNapi object" + << std::endl + << std::flush; + logger.error_log(__func__, + "Failed to convert config string to cobhan buffer"); + return; + } + + std::cerr << "SetupAsherahSync calling SetupJson(Go)" << std::endl + << std::flush; + + config.DebugPrintStdErr(); + // extern GoInt32 SetupJson(void* configJson); + GoInt32 result = SetupJson(config); + EndSetupAsherah(result, product_id_length, service_name_length); + } catch (const std::exception &e) { + logger.error_log(__func__, e.what()); + std::cerr << "SetupAsherahSync exception: " << e.what() << std::endl + << std::flush; } + } - void EndSetupAsherah(GoInt32 result, size_t product_id_length, - size_t service_name_length) { - if (unlikely(result < 0)) { - logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); - return; - } + Napi::Value SetupAsherahAsync(const Napi::CallbackInfo &info) { + const Napi::Env &env = info.Env(); + try { + Napi::String config_string; + size_t product_id_length; + size_t service_name_length; - est_intermediate_key_overhead = product_id_length + service_name_length; + BeginSetupAsherah(env, __func__, info, config_string, product_id_length, + service_name_length); - std::cerr << "******************* Setting setup_state to 1" << std::endl << std::flush; - auto old_setup_state = setup_state.exchange(1, std::memory_order_acq_rel); - if (unlikely(old_setup_state != 0)) { - logger.log_error_and_throw(__func__, "lost race to mark setup_state!"); - return; - } + CobhanBufferNapi config(env, config_string); + if (env.IsExceptionPending()) { + logger.error_log(__func__, + "Failed to convert config string to cobhan buffer"); + return env.Undefined(); + } + + auto worker = new SetupAsherahWorker(env, this, config, product_id_length, + service_name_length); + worker->Queue(); + return worker->Promise(); + } catch (const std::exception &e) { + logger.error_log(__func__, e.what()); + std::cerr << "SetupAsherahAsync exception: " << e.what() << std::endl + << std::flush; + return env.Undefined(); } + } - void SetupAsherahSync(const Napi::CallbackInfo &info) { - try { - const Napi::Env &env = info.Env(); - - Napi::String config_string; - size_t product_id_length; - size_t service_name_length; - - std::cerr << "SetupAsherahSync called" << std::endl << std::flush; - logger.error_log(__func__, "SetupAsherahSync called"); - - BeginSetupAsherah(env, __func__, info, config_string, - product_id_length, service_name_length); - - - char *config_cbuffer; - std::cerr << "Calculate allocation size by using StringToAllocationSize" << std::endl << std::flush; - size_t config_cbuffer_size = CobhanBufferNapi::StringToAllocationSize(env, config_string); - if (unlikely(config_cbuffer_size == 0)) { - std::cerr << "Failed to calculate allocation size for config string" << std::endl << std::flush; - logger.error_log(__func__, "Failed to calculate allocation size for config string"); - return; - } - std::cerr << "Allocate cbuffer using ALLOCATE_CBUFFER macro" << std::endl << std::flush; - SCOPED_ALLOCATE_BUFFER(logger, config_cbuffer, config_cbuffer_size, maximum_stack_alloc_size, __func__); - - std::cerr << "Create config CobhanBufferNapi object" << std::endl << std::flush; - CobhanBufferNapi config(env, config_string, config_cbuffer, config_cbuffer_size); - if (env.IsExceptionPending()) { - std::cerr << "Error creating the config CobhanBufferNapi object" << std::endl << std::flush; - logger.error_log(__func__, - "Failed to convert config string to cobhan buffer"); - return; - } - - std::cerr << "SetupAsherahSync calling SetupJson(Go)" << std::endl << std::flush; - - config.DebugPrintStdErr(); - // extern GoInt32 SetupJson(void* configJson); - GoInt32 result = SetupJson(config); - EndSetupAsherah(result, product_id_length, service_name_length); - } catch (const std::exception &e) { - logger.error_log(__func__, e.what()); - std::cerr << "SetupAsherahSync exception: " << e.what() << std::endl << std::flush; - } + bool BeginEncryptToJson(const Napi::Env &env, const char *func_name, + const Napi::CallbackInfo &info, + Napi::String &partition_id, Napi::Value &input) { + if (unlikely(setup_state.load(std::memory_order_acquire) == 0)) { + logger.log_error_and_throw(func_name, "setup() not called"); + return env.Undefined(); } - Napi::Value SetupAsherahAsync(const Napi::CallbackInfo &info) { - const Napi::Env &env = info.Env(); - try { - Napi::String config_string; - size_t product_id_length; - size_t service_name_length; - - BeginSetupAsherah(env, __func__, info, config_string, - product_id_length, service_name_length); - - CobhanBufferNapi config(env, config_string); - if (env.IsExceptionPending()) { - logger.error_log(__func__, - "Failed to convert config string to cobhan buffer"); - return env.Undefined(); - } - - auto worker = new SetupAsherahWorker(env, this, config, product_id_length, service_name_length); - worker->Queue(); - return worker->Promise(); - } catch (const std::exception &e) { - logger.error_log(__func__, e.what()); - std::cerr << "SetupAsherahAsync exception: " << e.what() << std::endl << std::flush; - return env.Undefined(); - } + if (unlikely(info.Length() < 2)) { + logger.log_error_and_throw(func_name, "Wrong number of arguments"); + return env.Undefined(); } - bool BeginEncryptToJson(const Napi::Env &env, const char *func_name, const Napi::CallbackInfo &info, - Napi::String &partition_id, Napi::Value &input) { - if (unlikely(setup_state.load(std::memory_order_acquire) == 0)) { - logger.log_error_and_throw(func_name, "setup() not called"); - return env.Undefined(); - } - - if (unlikely(info.Length() < 2)) { - logger.log_error_and_throw(func_name, "Wrong number of arguments"); - return env.Undefined(); - } - - if (unlikely(!info[0].IsString() || !(info[1].IsString() || info[1].IsBuffer()))) { - logger.log_error_and_throw(func_name, "Wrong argument types"); - return env.Undefined(); - } - - partition_id = info[0].As(); - input = info[1].As(); - return true; + if (unlikely(!info[0].IsString() || + !(info[1].IsString() || info[1].IsBuffer()))) { + logger.log_error_and_throw(func_name, "Wrong argument types"); + return env.Undefined(); } - bool EndEncryptToJson(Napi::Env env, CobhanBufferNapi &output, GoInt32 result, - Napi::String &output_string) { - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "Returning from asherah-cobhan EncryptToJson"); - } - - if (unlikely(result < 0)) { - logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); - return false; - } - - output_string = output.ToString(env); - if (env.IsExceptionPending()) { - logger.error_log(__func__, "Failed to convert output buffer to Napi::String"); - return false; - } - - return true; - } - - // This is the exported sync Encrypt function - Napi::Value EncryptSync(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - Napi::String output_string; - try { - Napi::String partition_id_string; - Napi::Value input_value; - std::cerr << "EncryptSync called" << std::endl << std::flush; - if (!BeginEncryptToJson(env, __func__, info, partition_id_string, input_value)) { - logger.error_log(__func__, "Failed to BeginEncryptToJson"); - return env.Undefined(); - } - - - std::cerr << "EncryptSync marshal input parameters" << std::endl << std::flush; - CobhanBufferNapi partition_id(env, partition_id_string); - CobhanBufferNapi input(env, input_value); - - size_t partition_id_data_len_bytes = partition_id.get_data_len_bytes(); - size_t input_data_len_bytes = input.get_data_len_bytes(); - size_t asherah_output_size_bytes = EstimateAsherahOutputSize(input_data_len_bytes, - partition_id_data_len_bytes); - if (unlikely(asherah_output_size_bytes == 0)) { - logger.error_log(__func__, "Failed to estimate output size"); - return env.Undefined(); - } - - std::cerr << "EncryptSync create output buffer" << std::endl << std::flush; - char *output_cobhan_buffer; - size_t output_size_bytes = CobhanBuffer::DataSizeToAllocationSize(asherah_output_size_bytes); - SCOPED_ALLOCATE_BUFFER(logger, output_cobhan_buffer, output_size_bytes, - maximum_stack_alloc_size, __func__); - CobhanBufferNapi output(output_cobhan_buffer, output_size_bytes); - - std::cerr << "EncryptSync Calling EncryptToJson" << std::endl << std::flush; - GoInt32 result = EncryptToJson(partition_id, input, output); - - std::cerr << "EncryptSync Calling EndEncryptToJson" << std::endl << std::flush; - if (!EndEncryptToJson(info.Env(), output, result, output_string)) { - logger.error_log(__func__, "Failed to EndEncryptToJson"); - return env.Undefined(); - } - - std::cerr << "EncryptSync EndEncryptToJson finished" << std::endl << std::flush; - } catch (const std::exception &e) { - logger.error_log(__func__, e.what()); - std::cerr << "EncryptSync exception: " << e.what() << std::endl << std::flush; - return env.Undefined(); - } + partition_id = info[0].As(); + input = info[1].As(); + return true; + } - std::cerr << "EncryptSync finished" << std::endl << std::flush; - return output_string; - } - - // This is the exported async Encrypt function - Napi::Value EncryptAsync(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - try { - Napi::String partition_id_string; - Napi::Value input_value; - if (!BeginEncryptToJson(env, __func__, info, partition_id_string, input_value)) { - logger.error_log(__func__, "Failed to BeginEncryptToJson"); - return env.Undefined(); - } - - CobhanBufferNapi partition_id(env, partition_id_string); - CobhanBufferNapi input(env, input_value); - - size_t partition_id_data_len_bytes = partition_id.get_data_len_bytes(); - size_t input_data_len_bytes = input.get_data_len_bytes(); - size_t asherah_output_size_bytes = EstimateAsherahOutputSize(input_data_len_bytes, - partition_id_data_len_bytes); - if (unlikely(asherah_output_size_bytes == 0)) { - logger.error_log(__func__, "Failed to estimate output size"); - return env.Undefined(); - } - - CobhanBufferNapi output(asherah_output_size_bytes); - - auto worker = new EncryptAsherahWorker(info.Env(), this, partition_id, input, output); - worker->Queue(); - return worker->Promise(); - } catch (const std::exception &e) { - logger.error_log(__func__, e.what()); - std::cerr << "EncryptAsync exception: " << e.what() << std::endl << std::flush; - return env.Undefined(); - } + bool EndEncryptToJson(Napi::Env env, CobhanBufferNapi &output, GoInt32 result, + Napi::String &output_string) { + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "Returning from asherah-cobhan EncryptToJson"); } - bool BeginDecryptFromJson(const Napi::Env &, const char *func_name, const Napi::CallbackInfo &info, - Napi::String &partition_id, Napi::Value &input) { + if (unlikely(result < 0)) { + logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); + return false; + } - if (unlikely(setup_state.load(std::memory_order_acquire) == 0)) { - logger.log_error_and_throw(func_name, "setup() not called"); - return false; - } + output_string = output.ToString(env); + if (env.IsExceptionPending()) { + logger.error_log(__func__, + "Failed to convert output buffer to Napi::String"); + return false; + } - if (unlikely(info.Length() < 2)) { - logger.log_error_and_throw(func_name, "Wrong number of arguments"); - return false; - } + return true; + } - if (unlikely(!info[0].IsString() || !(info[1].IsString() || info[1].IsBuffer()))) { - logger.log_error_and_throw(func_name, "Wrong argument types"); - return false; - } + bool EndEncryptToJson(Napi::Env env, CobhanBufferNapi &output, GoInt32 result, + Napi::Buffer &output_buffer) { + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "Returning from asherah-cobhan EncryptToJson"); + } - partition_id = info[0].As(); - input = info[1].As(); - return true; + if (unlikely(result < 0)) { + logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); + return false; } - bool EndDecryptFromJson(Napi::Env &env, CobhanBufferNapi &output, GoInt32 result, - Napi::Buffer &output_buffer) { - if (unlikely(result < 0)) { - logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); - return false; - } + output_buffer = output.ToBuffer(env); + if (env.IsExceptionPending()) { + logger.error_log(__func__, + "Failed to convert output buffer to Napi::String"); + return false; + } - output_buffer = output.ToBuffer(env); - if (env.IsExceptionPending()) { - logger.error_log(__func__, "Failed to convert output buffer to Napi::String"); - return false; - } + return true; + } - return true; + // This is the exported sync Encrypt function + Napi::Value EncryptSync(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + Napi::String output_string; + try { + Napi::String partition_id_string; + Napi::Value input_value; + std::cerr << "EncryptSync called" << std::endl << std::flush; + if (!BeginEncryptToJson(env, __func__, info, partition_id_string, + input_value)) { + logger.error_log(__func__, "Failed to BeginEncryptToJson"); + return env.Undefined(); + } + + std::cerr << "EncryptSync marshal input parameters" << std::endl + << std::flush; + CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi input(env, input_value); + + size_t partition_id_data_len_bytes = partition_id.get_data_len_bytes(); + size_t input_data_len_bytes = input.get_data_len_bytes(); + size_t asherah_output_size_bytes = EstimateAsherahOutputSize( + input_data_len_bytes, partition_id_data_len_bytes); + if (unlikely(asherah_output_size_bytes == 0)) { + logger.error_log(__func__, "Failed to estimate output size"); + return env.Undefined(); + } + + std::cerr << "EncryptSync create output buffer" << std::endl + << std::flush; + char *output_cobhan_buffer; + size_t output_size_bytes = + CobhanBuffer::DataSizeToAllocationSize(asherah_output_size_bytes); + SCOPED_ALLOCATE_BUFFER(logger, output_cobhan_buffer, output_size_bytes, + maximum_stack_alloc_size, __func__); + CobhanBufferNapi output(output_cobhan_buffer, output_size_bytes); + + std::cerr << "EncryptSync Calling EncryptToJson" << std::endl + << std::flush; + GoInt32 result = EncryptToJson(partition_id, input, output); + + std::cerr << "EncryptSync Calling EndEncryptToJson" << std::endl + << std::flush; + if (!EndEncryptToJson(env, output, result, output_string)) { + logger.error_log(__func__, "Failed to EndEncryptToJson"); + return env.Undefined(); + } + + std::cerr << "EncryptSync EndEncryptToJson finished" << std::endl + << std::flush; + } catch (const std::exception &e) { + logger.error_log(__func__, e.what()); + std::cerr << "EncryptSync exception: " << e.what() << std::endl + << std::flush; + return env.Undefined(); } - bool EndDecryptFromJson(Napi::Env &env, CobhanBufferNapi &output, GoInt32 result, - Napi::String &output_string) { - if (unlikely(result < 0)) { - logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); - return false; - } + std::cerr << "EncryptSync finished" << std::endl << std::flush; + return output_string; + } - output_string = output.ToString(env); - if (env.IsExceptionPending()) { - logger.error_log(__func__, "Failed to convert output buffer to Napi::String"); - return false; - } + // This is the exported async Encrypt function + Napi::Value EncryptAsync(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + try { + Napi::String partition_id_string; + Napi::Value input_value; + if (!BeginEncryptToJson(env, __func__, info, partition_id_string, + input_value)) { + logger.error_log(__func__, "Failed to BeginEncryptToJson"); + return env.Undefined(); + } - return true; - } - - Napi::Value DecryptSync(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - Napi::Object output_value; - try { - Napi::String partition_id_string; - Napi::Value input_value; - std::cerr << "DecryptSync called" << std::endl << std::flush; - - std::cerr << "\nDecryptSync BeginDecryptFromJson" << std::endl << std::flush; - BeginDecryptFromJson(env, __func__, info, partition_id_string, input_value); - - std::cerr << "\nDecryptSync marshal input parameter partition_id" << std::endl << std::flush; - CobhanBufferNapi partition_id(env, partition_id_string); - - std::cerr << "\nDecryptSync marshal input parameter input" << std::endl << std::flush; - CobhanBufferNapi input(env, input_value); - - std::cerr << "DecryptSync create output buffer" << std::endl << std::flush; - char *output_cobhan_buffer; - size_t output_size_bytes = CobhanBuffer::DataSizeToAllocationSize(input.get_data_len_bytes()); - SCOPED_ALLOCATE_BUFFER(logger, output_cobhan_buffer, output_size_bytes, - maximum_stack_alloc_size, __func__); - CobhanBufferNapi output(output_cobhan_buffer, output_size_bytes); - - std::cerr << "\nDecryptSync Calling DecryptFromJson" << std::endl << std::flush; - // extern GoInt32 DecryptFromJson(void* partitionIdPtr, void* jsonPtr, void* dataPtr); - GoInt32 result = DecryptFromJson(partition_id, input, output); - std::cerr << "\nDecryptSync Returned from DecryptFromJson" << std::endl << std::flush; - - if (unlikely(result < 0)) { - logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); - return env.Undefined(); - } - - std::cerr << "\nDecryptSync Calling ToBuffer" << std::endl << std::flush; - output_value = output.ToBuffer(env); // NOLINT(*-slicing) - if (env.IsExceptionPending()) { - std::cerr << "DecryptSync Failed to convert output buffer to Napi::String" << std::endl << std::flush; - logger.error_log(__func__, "Failed to convert output buffer to Napi::String"); - return env.Undefined(); - } - std::cerr << "\nDecryptSync ToBuffer finished" << std::endl << std::flush; - } catch (const std::exception &e) { - std::cerr << "DecryptSync exception: " << e.what() << std::endl << std::flush; - logger.error_log(__func__, e.what()); - return env.Undefined(); - } - std::cerr << "\nDecryptSync finished" << std::endl << std::flush; - return output_value; - } + CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi input(env, input_value); - Napi::Value DecryptAsync(const Napi::CallbackInfo &info) { - Napi::Env env = info.Env(); - try { - Napi::String partition_id_string; - Napi::Value input_value; - BeginDecryptFromJson(env, __func__, info, partition_id_string, input_value); - - CobhanBufferNapi partition_id(env, partition_id_string); - CobhanBufferNapi input(env, input_value); - - CobhanBufferNapi output(input.get_data_len_bytes()); - auto worker = new DecryptFromJsonToBufferWorker(info.Env(), this, partition_id, input, - output); - worker->Queue(); - return worker->Promise(); - } catch (const std::exception &e) { - logger.error_log(__func__, e.what()); - std::cerr << "DecryptAsync exception: " << e.what() << std::endl << std::flush; - return env.Undefined(); - } + size_t partition_id_data_len_bytes = partition_id.get_data_len_bytes(); + size_t input_data_len_bytes = input.get_data_len_bytes(); + size_t asherah_output_size_bytes = EstimateAsherahOutputSize( + input_data_len_bytes, partition_id_data_len_bytes); + if (unlikely(asherah_output_size_bytes == 0)) { + logger.error_log(__func__, "Failed to estimate output size"); + return env.Undefined(); + } + + CobhanBufferNapi output(asherah_output_size_bytes); + + auto worker = new EncryptAsherahWorker(info.Env(), this, partition_id, + input, output); + worker->Queue(); + return worker->Promise(); + } catch (const std::exception &e) { + logger.error_log(__func__, e.what()); + std::cerr << "EncryptAsync exception: " << e.what() << std::endl + << std::flush; + return env.Undefined(); } + } - void BeginShutdownAsherah() { - std::cerr << "BeginShutdownAsherah called" << std::endl << std::flush; - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "called"); - } + bool BeginDecryptFromJson(const Napi::Env &, const char *func_name, + const Napi::CallbackInfo &info, + Napi::String &partition_id, Napi::Value &input) { - if (unlikely(setup_state.load(std::memory_order_acquire) == 0)) { - std::cerr << "BeginShutdownAsherah - setup_state already 0" << std::endl << std::flush; - logger.log_error_and_throw(__func__, "setup() not called"); - return; - } + if (unlikely(setup_state.load(std::memory_order_acquire) == 0)) { + logger.log_error_and_throw(func_name, "setup() not called"); + return false; + } - std::cerr << "BeginShutdownAsherah finished" << std::endl << std::flush; + if (unlikely(info.Length() < 2)) { + logger.log_error_and_throw(func_name, "Wrong number of arguments"); + return false; } - void EndShutdownAsherah() { - std::cerr << "EndShutdownAsherah called" << std::endl << std::flush; - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "called"); - } + if (unlikely(!info[0].IsString() || + !(info[1].IsString() || info[1].IsBuffer()))) { + logger.log_error_and_throw(func_name, "Wrong argument types"); + return false; + } - std::cerr << "******************* Setting setup_state to 0" << std::endl << std::flush; - std::cerr << "ShutdownAsherah before atomic exchange of setup_state" << std::endl << std::flush; - auto old_setup_state = setup_state.exchange(0, std::memory_order_acq_rel); - if (unlikely(old_setup_state == 0)) { - std::cerr << "ShutdownAsherah - setup_state already 0" << std::endl << std::flush; - logger.log_error_and_throw(__func__, "setup() not called"); - return; - } + partition_id = info[0].As(); + input = info[1].As(); + return true; + } - std::cerr << "EndShutdownAsherah finished" << std::endl << std::flush; + bool EndDecryptFromJson(Napi::Env &env, CobhanBufferNapi &output, + GoInt32 result, + Napi::Buffer &output_buffer) { + if (unlikely(result < 0)) { + logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); + return false; } - void ShutdownAsherah() { - std::cerr << "ShutdownAsherah called" << std::endl << std::flush; - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "Calling asherah-cobhan Shutdown"); - } + output_buffer = output.ToBuffer(env); + if (env.IsExceptionPending()) { + logger.error_log(__func__, + "Failed to convert output buffer to Napi::String"); + return false; + } - std::cerr << "ShutdownAsherah before calling asherah-cobhan Shutdown" << std::endl << std::flush; - // extern void Shutdown(); - Shutdown(); - std::cerr << "ShutdownAsherah after calling asherah-cobhan Shutdown" << std::endl << std::flush; + return true; + } - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "Returned from asherah-cobhan Shutdown"); - } + bool EndDecryptFromJson(Napi::Env &env, CobhanBufferNapi &output, + GoInt32 result, Napi::String &output_string) { + if (unlikely(result < 0)) { + logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); + return false; } - void ShutdownAsherahSync(const Napi::CallbackInfo &) { - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "called"); - } - BeginShutdownAsherah(); - ShutdownAsherah(); - EndShutdownAsherah(); + output_string = output.ToString(env); + if (env.IsExceptionPending()) { + logger.error_log(__func__, + "Failed to convert output buffer to Napi::String"); + return false; } - Napi::Value ShutdownAsherahAsync(const Napi::CallbackInfo &info) { - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "called"); - } + return true; + } - BeginShutdownAsherah(); - auto worker = new ShutdownAsherahWorker(info.Env(), this); - worker->Queue(); - return worker->Promise(); + Napi::Value DecryptSync(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + Napi::Object output_value; + try { + Napi::String partition_id_string; + Napi::Value input_value; + std::cerr << "DecryptSync called" << std::endl << std::flush; + + std::cerr << "\nDecryptSync BeginDecryptFromJson" << std::endl + << std::flush; + BeginDecryptFromJson(env, __func__, info, partition_id_string, + input_value); + + std::cerr << "\nDecryptSync marshal input parameter partition_id" + << std::endl + << std::flush; + CobhanBufferNapi partition_id(env, partition_id_string); + + std::cerr << "\nDecryptSync marshal input parameter input" << std::endl + << std::flush; + CobhanBufferNapi input(env, input_value); + + std::cerr << "DecryptSync create output buffer" << std::endl + << std::flush; + char *output_cobhan_buffer; + size_t output_size_bytes = + CobhanBuffer::DataSizeToAllocationSize(input.get_data_len_bytes()); + SCOPED_ALLOCATE_BUFFER(logger, output_cobhan_buffer, output_size_bytes, + maximum_stack_alloc_size, __func__); + CobhanBufferNapi output(output_cobhan_buffer, output_size_bytes); + + std::cerr << "\nDecryptSync Calling DecryptFromJson" << std::endl + << std::flush; + // extern GoInt32 DecryptFromJson(void* partitionIdPtr, void* jsonPtr, + // void* dataPtr); + GoInt32 result = DecryptFromJson(partition_id, input, output); + std::cerr << "\nDecryptSync Returned from DecryptFromJson" << std::endl + << std::flush; + + if (unlikely(result < 0)) { + logger.log_error_and_throw(__func__, + AsherahCobhanErrorToString(result)); + return env.Undefined(); + } + + std::cerr << "\nDecryptSync Calling ToBuffer" << std::endl << std::flush; + output_value = output.ToBuffer(env); // NOLINT(*-slicing) + if (env.IsExceptionPending()) { + std::cerr + << "DecryptSync Failed to convert output buffer to Napi::String" + << std::endl + << std::flush; + logger.error_log(__func__, + "Failed to convert output buffer to Napi::String"); + return env.Undefined(); + } + std::cerr << "\nDecryptSync ToBuffer finished" << std::endl << std::flush; + } catch (const std::exception &e) { + std::cerr << "DecryptSync exception: " << e.what() << std::endl + << std::flush; + logger.error_log(__func__, e.what()); + return env.Undefined(); } + std::cerr << "\nDecryptSync finished" << std::endl << std::flush; + return output_value; + } - class SetupAsherahWorker : public Napi::AsyncWorker { - public: - SetupAsherahWorker(Napi::Env env, Asherah *instance, - CobhanBufferNapi &config, - size_t product_id_length, size_t service_name_length) - : AsyncWorker(env), asherah(instance), config(std::move(config)), product_id_length(product_id_length), - service_name_length(service_name_length), deferred(env), result(-1) {} - - Napi::Promise Promise() { return deferred.Promise(); } - - void Execute() override { - result = SetupJson(config); - } + Napi::Value DecryptAsync(const Napi::CallbackInfo &info) { + Napi::Env env = info.Env(); + try { + Napi::String partition_id_string; + Napi::Value input_value; + BeginDecryptFromJson(env, __func__, info, partition_id_string, + input_value); + + CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi input(env, input_value); + + CobhanBufferNapi output(input.get_data_len_bytes()); + auto worker = new DecryptFromJsonToBufferWorker( + info.Env(), this, partition_id, input, output); + worker->Queue(); + return worker->Promise(); + } catch (const std::exception &e) { + logger.error_log(__func__, e.what()); + std::cerr << "DecryptAsync exception: " << e.what() << std::endl + << std::flush; + return env.Undefined(); + } + } - void OnOK() override { - try { - asherah->EndSetupAsherah(result, product_id_length, service_name_length); - deferred.Resolve(Napi::Number::New(Env(), result)); - } catch (const std::exception &e) { - deferred.Reject(Napi::Error::New(Env(), e.what()).Value()); - } - } + void BeginShutdownAsherah() { + std::cerr << "BeginShutdownAsherah called" << std::endl << std::flush; + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "called"); + } - void OnError(Napi::Error const &error) override { - deferred.Reject(error.Value()); - } + if (unlikely(setup_state.load(std::memory_order_acquire) == 0)) { + std::cerr << "BeginShutdownAsherah - setup_state already 0" << std::endl + << std::flush; + logger.log_error_and_throw(__func__, "setup() not called"); + return; + } - private: - Asherah *asherah; - CobhanBufferNapi config; - size_t product_id_length; - size_t service_name_length; - Napi::Promise::Deferred deferred; - GoInt32 result; - }; + std::cerr << "BeginShutdownAsherah finished" << std::endl << std::flush; + } - class EncryptAsherahWorker : public Napi::AsyncWorker { - public: - EncryptAsherahWorker(const Napi::Env &env, Asherah *instance, - CobhanBufferNapi &partition_id, - CobhanBufferNapi &input, - CobhanBufferNapi &output) - : AsyncWorker(env), asherah(instance), partition_id(std::move(partition_id)), input(std::move(input)), - output(std::move(output)), deferred(env), result(-1) {} + void EndShutdownAsherah() { + std::cerr << "EndShutdownAsherah called" << std::endl << std::flush; + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "called"); + } - Napi::Promise Promise() { return deferred.Promise(); } + std::cerr << "******************* Setting setup_state to 0" << std::endl + << std::flush; + std::cerr << "ShutdownAsherah before atomic exchange of setup_state" + << std::endl + << std::flush; + auto old_setup_state = setup_state.exchange(0, std::memory_order_acq_rel); + if (unlikely(old_setup_state == 0)) { + std::cerr << "ShutdownAsherah - setup_state already 0" << std::endl + << std::flush; + logger.log_error_and_throw(__func__, "setup() not called"); + return; + } - void Execute() override { - // extern GoInt32 EncryptToJson(void* partitionIdPtr, void* dataPtr, void* jsonPtr); - result = EncryptToJson(partition_id, input, output); - } + std::cerr << "EndShutdownAsherah finished" << std::endl << std::flush; + } - void OnOK() override { - Napi::String output_string; - if (!asherah->EndEncryptToJson(Env(), output, result, output_string)) { - deferred.Reject(Napi::String::New(Env(), "Failed to EndEncryptToJson " + - std::string(AsherahCobhanErrorToString(result)))); - return; - } + void ShutdownAsherah() { + std::cerr << "ShutdownAsherah called" << std::endl << std::flush; + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "Calling asherah-cobhan Shutdown"); + } - deferred.Resolve(output_string); - } + std::cerr << "ShutdownAsherah before calling asherah-cobhan Shutdown" + << std::endl + << std::flush; + // extern void Shutdown(); + Shutdown(); + std::cerr << "ShutdownAsherah after calling asherah-cobhan Shutdown" + << std::endl + << std::flush; - void OnError(Napi::Error const &error) override { - deferred.Reject(error.Value()); - } + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "Returned from asherah-cobhan Shutdown"); + } + } - private: - Asherah *asherah; - CobhanBufferNapi partition_id; - CobhanBufferNapi input; - CobhanBufferNapi output; - Napi::Promise::Deferred deferred; - GoInt32 result; - }; + void ShutdownAsherahSync(const Napi::CallbackInfo &) { + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "called"); + } + BeginShutdownAsherah(); + ShutdownAsherah(); + EndShutdownAsherah(); + } - class DecryptFromJsonToBufferWorker : public Napi::AsyncWorker { - public: - DecryptFromJsonToBufferWorker(const Napi::Env &env, Asherah *instance, - CobhanBufferNapi &partition_id, - CobhanBufferNapi &input, - CobhanBufferNapi &output) - : AsyncWorker(env), asherah(instance), partition_id(std::move(partition_id)), input(std::move(input)), - output(std::move(output)), deferred(env), result(-1) {} + Napi::Value ShutdownAsherahAsync(const Napi::CallbackInfo &info) { + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "called"); + } - Napi::Promise Promise() { return deferred.Promise(); } + BeginShutdownAsherah(); + auto worker = new ShutdownAsherahWorker(info.Env(), this); + worker->Queue(); + return worker->Promise(); + } - void Execute() override { - // extern GoInt32 DecryptFromJson(void* partitionIdPtr, void* jsonPtr, void* dataPtr); - result = DecryptFromJson(partition_id, input, output); - } + class SetupAsherahWorker : public AsherahAsyncWorker { + public: + SetupAsherahWorker(Napi::Env env, Asherah *instance, + CobhanBufferNapi &config, size_t product_id_length, + size_t service_name_length) + : AsherahAsyncWorker(env, instance), config(std::move(config)), + product_id_length(product_id_length), + service_name_length(service_name_length) {} + + GoInt32 ExecuteTask() override { return SetupJson(config); } + + Napi::Value OnOKTask() override { + asherah->EndSetupAsherah(result, product_id_length, service_name_length); + return Env().Undefined(); + } + + private: + CobhanBufferNapi config; + size_t product_id_length; + size_t service_name_length; + }; + + class EncryptAsherahWorker : public AsherahAsyncWorker { + public: + EncryptAsherahWorker(const Napi::Env &env, Asherah *instance, + CobhanBufferNapi &partition_id, + CobhanBufferNapi &input, CobhanBufferNapi &output) + : AsherahAsyncWorker(env, instance), + partition_id(std::move(partition_id)), input(std::move(input)), + output(std::move(output)) {} + + // extern GoInt32 EncryptToJson(void* partitionIdPtr, void* dataPtr, + // void* jsonPtr); + GoInt32 ExecuteTask() override { + return EncryptToJson(partition_id, input, output); + } + + Napi::Value OnOKTask() override { + Napi::String output_string; + if (!asherah->EndEncryptToJson(Env(), output, result, output_string)) { + throw std::runtime_error( + "Failed to EndEncryptToJson " + + std::string(AsherahCobhanErrorToString(result))); + } + return output_string; + } + + private: + CobhanBufferNapi partition_id; + CobhanBufferNapi input; + CobhanBufferNapi output; + }; + + class DecryptFromJsonToBufferWorker : public AsherahAsyncWorker { + public: + DecryptFromJsonToBufferWorker(const Napi::Env &env, Asherah *instance, + CobhanBufferNapi &partition_id, + CobhanBufferNapi &input, + CobhanBufferNapi &output) + : AsherahAsyncWorker(env, instance), + partition_id(std::move(partition_id)), input(std::move(input)), + output(std::move(output)) {} + + // extern GoInt32 DecryptFromJson(void* partitionIdPtr, void* jsonPtr, + // void* dataPtr); + GoInt32 ExecuteTask() override { + return DecryptFromJson(partition_id, input, output); + } + + Napi::Value OnOKTask() override { + Napi::Env env = Env(); + Napi::Buffer output_buffer; + if (!asherah->EndDecryptFromJson(env, output, result, output_buffer)) { + return Napi::String::New( + env, "Failed to EndDecryptFromJson " + + std::string(AsherahCobhanErrorToString(result))); + } + return output_buffer; + } + + private: + CobhanBufferNapi partition_id; + CobhanBufferNapi input; + CobhanBufferNapi output; + }; + + class ShutdownAsherahWorker : public AsherahAsyncWorker { + public: + using AsherahAsyncWorker::AsherahAsyncWorker; + + // extern void Shutdown(); + GoInt32 ExecuteTask() override { + std::cerr << "ShutdownAsherahWorker ExecuteTask called" << std::endl + << std::flush; + Shutdown(); + return 0; + } + + Napi::Value OnOKTask() override { + std::cerr << "ShutdownAsherahWorker OnOK called" << std::endl + << std::flush; + asherah->EndShutdownAsherah(); + return Env().Undefined(); + } + }; + + void SetMaxStackAllocItemSize(const Napi::CallbackInfo &info) { + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "called"); + } - void OnOK() override { - Napi::Env env = Env(); - Napi::Buffer output_buffer; - if (!asherah->EndDecryptFromJson(env, output, result, output_buffer)) { - deferred.Reject(Napi::String::New(Env(), "Failed to EndDecryptFromJson " + - std::string(AsherahCobhanErrorToString(result)))); - return; - } + if (unlikely(info.Length() < 1)) { + logger.log_error_and_throw(__func__, "Wrong number of arguments"); + return; + } - deferred.Resolve(output.ToBuffer(Env())); - } + Napi::Number item_size = info[0].ToNumber(); + auto new_size = (size_t)item_size.Int32Value(); - void OnError(Napi::Error const &error) override { - deferred.Reject(error.Value()); - } - private: - Asherah *asherah; - CobhanBufferNapi partition_id; - CobhanBufferNapi input; - CobhanBufferNapi output; - Napi::Promise::Deferred deferred; - GoInt32 result; - }; + maximum_stack_alloc_size = new_size; + } - class ShutdownAsherahWorker : public Napi::AsyncWorker { - public: - ShutdownAsherahWorker(Napi::Env env, Asherah *instance) - : AsyncWorker(env), asherah(instance), deferred(env) {} + void SetSafetyPaddingOverhead(const Napi::CallbackInfo &info) { - Napi::Promise Promise() { return deferred.Promise(); } + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "called"); + } - void Execute() override { - std::cerr << "ShutdownAsherahWorker Execute called" << std::endl << std::flush; - // extern void Shutdown(); - Shutdown(); - } + if (unlikely(info.Length() < 1)) { + logger.log_error_and_throw(__func__, "Wrong number of arguments"); + return; + } - void OnOK() override { - std::cerr << "ShutdownAsherahWorker OnOK called" << std::endl << std::flush; - asherah->EndShutdownAsherah(); - deferred.Resolve(Env().Null()); - } + // Napi::Number safety_padding_number = info[0].ToNumber(); + // auto new_safety_padding_bytes = (size_t) + // safety_padding_number.Int32Value(); - void OnError(Napi::Error const &error) override { - std::cerr << "ShutdownAsherahWorker OnError called" << std::endl << std::flush; - asherah->EndShutdownAsherah(); - deferred.Reject(error.Value()); - } + // set_safety_padding_bytes(new_safety_padding_bytes); + } - private: - Asherah *asherah; - Napi::Promise::Deferred deferred; - }; + __attribute__((always_inline)) inline size_t + EstimateAsherahOutputSize(size_t data_byte_len, size_t partition_byte_len) { + const size_t est_encryption_overhead = 48; + const size_t est_envelope_overhead = 185; + const double base64_overhead = 1.34; - void SetMaxStackAllocItemSize(const Napi::CallbackInfo &info) { - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "called"); - } + // Add one rather than using std::ceil to round up + size_t est_data_byte_len = + size_t(double(data_byte_len + est_encryption_overhead) * + base64_overhead) + + 1; - if (unlikely(info.Length() < 1)) { - logger.log_error_and_throw(__func__, "Wrong number of arguments"); - return; - } + size_t asherah_output_size_bytes = est_envelope_overhead + + est_intermediate_key_overhead + + partition_byte_len + est_data_byte_len; - Napi::Number item_size = info[0].ToNumber(); - auto new_size = (size_t) item_size.Int32Value(); + if (unlikely(verbose_flag)) { + std::string log_msg = + __func__ + ("(" + std::to_string(data_byte_len)) + ", " + + std::to_string(partition_byte_len) + + ") est_data_byte_len: " + std::to_string(est_data_byte_len) + + " asherah_output_size_bytes: " + + std::to_string(asherah_output_size_bytes); + logger.debug_log(__func__, log_msg); + } + return asherah_output_size_bytes; + } - maximum_stack_alloc_size = new_size; + __attribute__((always_inline)) static inline const char * + AsherahCobhanErrorToString(int32_t error) { + switch (error) { + case 0: + return "Success"; + case -1: + return "Cobhan error: NULL pointer"; + case -2: + return "Cobhan error: Buffer too large"; + case -3: + return "Cobhan error: Buffer too small"; + case -4: + return "Cobhan error: Copy failed"; + case -5: + return "Cobhan error: JSON decode failed"; + case -6: + return "Cobhan error: JSON encode failed"; + case -7: + return "Cobhan error: Invalid UTF-8"; + case -8: + return "Cobhan error: Read temp file failed"; + case -9: + return "Cobhan error: Write temp file failed"; + case -100: + return "Asherah error: Not initialized"; + case -101: + return "Asherah error: Already initialized"; + case -102: + return "Asherah error: Failed to get session"; + case -103: + return "Asherah error: Encrypt operation failed"; + case -104: + return "Asherah error: Decrypt operation failed"; + case -105: + return "Asherah error: Invalid configuration"; + default: + return "Unknown error"; } + } - void SetSafetyPaddingOverhead(const Napi::CallbackInfo &info) { - - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "called"); - } - - if (unlikely(info.Length() < 1)) { - logger.log_error_and_throw(__func__, "Wrong number of arguments"); - return; - } - - //Napi::Number safety_padding_number = info[0].ToNumber(); - //auto new_safety_padding_bytes = (size_t) safety_padding_number.Int32Value(); - - //set_safety_padding_bytes(new_safety_padding_bytes); + void SetLogHook(const Napi::CallbackInfo &info) { + if (unlikely(verbose_flag)) { + logger.debug_log(__func__, "called"); } - __attribute__((always_inline)) inline size_t - EstimateAsherahOutputSize(size_t data_byte_len, size_t partition_byte_len) { - const size_t est_encryption_overhead = 48; - const size_t est_envelope_overhead = 185; - const double base64_overhead = 1.34; - - // Add one rather than using std::ceil to round up - size_t est_data_byte_len = - size_t(double(data_byte_len + est_encryption_overhead) * - base64_overhead) + - 1; - - size_t asherah_output_size_bytes = est_envelope_overhead + - est_intermediate_key_overhead + - partition_byte_len + est_data_byte_len; - - if (unlikely(verbose_flag)) { - std::string log_msg = - __func__ + ("(" + std::to_string(data_byte_len)) + ", " + - std::to_string(partition_byte_len) + - ") est_data_byte_len: " + std::to_string(est_data_byte_len) + - " asherah_output_size_bytes: " + - std::to_string(asherah_output_size_bytes); - logger.debug_log(__func__, log_msg); - } - return asherah_output_size_bytes; - } - - __attribute__((always_inline)) static inline const char * - AsherahCobhanErrorToString(int32_t error) { - switch (error) { - case 0: - return "Success"; - case -1: - return "Cobhan error: NULL pointer"; - case -2: - return "Cobhan error: Buffer too large"; - case -3: - return "Cobhan error: Buffer too small"; - case -4: - return "Cobhan error: Copy failed"; - case -5: - return "Cobhan error: JSON decode failed"; - case -6: - return "Cobhan error: JSON encode failed"; - case -7: - return "Cobhan error: Invalid UTF-8"; - case -8: - return "Cobhan error: Read temp file failed"; - case -9: - return "Cobhan error: Write temp file failed"; - case -100: - return "Asherah error: Not initialized"; - case -101: - return "Asherah error: Already initialized"; - case -102: - return "Asherah error: Failed to get session"; - case -103: - return "Asherah error: Encrypt operation failed"; - case -104: - return "Asherah error: Decrypt operation failed"; - case -105: - return "Asherah error: Invalid configuration"; - default: - return "Unknown error"; - } + if (unlikely(info.Length() < 1)) { + logger.log_error_and_throw(__func__, "Wrong number of arguments"); + return; } - void SetLogHook(const Napi::CallbackInfo &info) { - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "called"); - } - - if (unlikely(info.Length() < 1)) { - logger.log_error_and_throw(__func__, "Wrong number of arguments"); - return; - } - - if (unlikely(!info[0].IsFunction())) { - logger.log_error_and_throw(__func__, "Wrong argument type"); - return; - } - - logger.set_log_hook(info[0].As()); + if (unlikely(!info[0].IsFunction())) { + logger.log_error_and_throw(__func__, "Wrong argument type"); + return; } - Napi::Value GetSetupStatus(const Napi::CallbackInfo &info) { // NOLINT(*-convert-member-functions-to-static) - return Napi::Boolean::New(info.Env(), - setup_state.load(std::memory_order_acquire) != 0); - } + logger.set_log_hook(info[0].As()); + } + + Napi::Value + GetSetupStatus(const Napi::CallbackInfo + &info) { // NOLINT(*-convert-member-functions-to-static) + return Napi::Boolean::New(info.Env(), + setup_state.load(std::memory_order_acquire) != 0); + } }; NODE_API_NAMED_ADDON('asherah', Asherah) - /* // This is the high level JavaScript Encrypt function Napi::Value Encrypt(const Napi::Env &env, @@ -818,8 +871,8 @@ NODE_API_NAMED_ADDON('asherah', Asherah) } size_t output_size_bytes; - if (!BeginEncryptToJson(env, __func__, partition_id_copied_bytes, input_copied_bytes, - partition_id_cobhan_buffer, input_cobhan_buffer, + if (!BeginEncryptToJson(env, __func__, partition_id_copied_bytes, + input_copied_bytes, partition_id_cobhan_buffer, input_cobhan_buffer, output_size_bytes)) { logger.error_log(__func__, "Failed to BeginEncryptToJson"); return env.Undefined(); @@ -854,129 +907,6 @@ NODE_API_NAMED_ADDON('asherah', Asherah) return output; } */ -/* - // This is the high level JavaScript EncryptString function - Napi::Value EncryptString(const Napi::Env &env, - const Napi::Value &partition_id_value, - const Napi::Value &input_value) { - Napi::String partition_id = partition_id_value.As(); - char *partition_id_cobhan_buffer; - size_t partition_id_copied_bytes; - NAPI_STRING_TO_CBUFFER( - env, logger, partition_id, partition_id_cobhan_buffer, - partition_id_copied_bytes, maximum_stack_alloc_size, __func__); - - if (partition_id_cobhan_buffer == nullptr || env.IsExceptionPending()) { - logger.error_log(__func__, - "Failed to convert partition_id to cobhan buffer"); - return env.Undefined(); - } - - Napi::String input = input_value.As(); - char *input_cobhan_buffer; - size_t input_copied_bytes; - NAPI_STRING_TO_CBUFFER(env, logger, input, input_cobhan_buffer, - input_copied_bytes, maximum_stack_alloc_size, - __func__); - if (input_cobhan_buffer == nullptr || env.IsExceptionPending()) { - logger.error_log(__func__, "Failed to convert input to cobhan buffer"); - return env.Undefined(); - } - - size_t output_size_bytes; - if (!BeginEncryptToJson(env, __func__, partition_id_copied_bytes, input_copied_bytes, - partition_id_cobhan_buffer, input_cobhan_buffer, - output_size_bytes)) { - logger.error_log(__func__, "Failed to BeginEncryptToJson"); - return env.Undefined(); - } - - char *output_cobhan_buffer; - ALLOCATE_CBUFFER(logger, output_cobhan_buffer, output_size_bytes, - maximum_stack_alloc_size, __func__); - - if (unlikely(output_cobhan_buffer == nullptr || env.IsExceptionPending())) { - logger.error_log(__func__, "Failed to allocate output buffer"); - return env.Undefined(); - } - - char *output_canary_ptr = get_canary_ptr(output_cobhan_buffer); - if (unlikely(!check_canary_ptr(__func__, logger, output_canary_ptr))) { - logger.log_error_and_throw( - __func__, "Failed initial canary check for output_cobhan_buffer"); - return env.Undefined(); - } - - GoInt32 result = ::EncryptToJson(partition_id_cobhan_buffer, - input_cobhan_buffer, output_cobhan_buffer); - - Napi::String output; - if (!EndEncryptToJson(env, output_cobhan_buffer, output_canary_ptr, result, - output)) { - logger.error_log(__func__, "Failed to EndEncryptToJson"); - return env.Undefined(); - } - - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "finished"); - } - - return output; - } -*/ -/* - // This is the exported sync EncryptString function - Napi::Value EncryptStringSync(const Napi::CallbackInfo &info) { - - if (unlikely(verbose_flag)) { - logger.debug_log(__func__, "called"); - } - - if (unlikely(setup_state.load(std::memory_order_relaxed) == 0)) { - logger.log_error_and_throw(__func__, "setup() not called"); - return info.Env().Undefined(); - } - - if (unlikely(info.Length() < 2)) { - logger.log_error_and_throw(__func__, "Wrong number of arguments"); - return info.Env().Undefined(); - } - - if (unlikely(!info[0].IsString() || !info[1].IsString())) { - logger.log_error_and_throw(__func__, "Wrong argument types"); - return info.Env().Undefined(); - } - - return EncryptString(info.Env(), info[0], info[1]); - } -*/ -/* - Napi::Value EncryptStringAsync(const Napi::CallbackInfo &info) { - if (unlikely(setup_state == 0)) { - logger.log_error_and_throw(__func__, "setup() not called"); - } - - if (unlikely(info.Length() < 2)) { - logger.log_error_and_throw(__func__, "Wrong number of arguments"); - } - - if (unlikely(!info[0].IsString() || !info[1].IsString())) { - logger.log_error_and_throw(__func__, "Wrong argument types"); - } - - auto worker = new EncryptStringAsherahWorker(info.Env(), this, - info[0], info[1]); worker->Queue(); - - return worker->Promise(); - } - - Napi::Env env, const char *func_name, - const Napi::CallbackInfo &info, - Napi::String &config_string, size_t &product_id_length, - size_t &service_name_length -*/ - - /* Napi::Value DecryptAsync(const Napi::CallbackInfo &info) { @@ -1103,14 +1033,14 @@ Napi::Value DecryptString(const Napi::Env &env, char *input_canary_ptr = get_canary_ptr(input_cobhan_buffer); if (unlikely(!check_canary_ptr(__func__, logger, input_canary_ptr))) { logger.log_error_and_throw( - __func__, "Failed initial canary check for input_cobhan_buffer"); - return env.Undefined(); + __func__, "Failed initial canary check for +input_cobhan_buffer"); return env.Undefined(); } const char *output_canary_ptr = get_canary_ptr(output_cobhan_buffer); if (unlikely(!check_canary_ptr(__func__, logger, output_canary_ptr))) { logger.log_error_and_throw( - __func__, "Failed initial canary check for output_cobhan_buffer"); - return env.Undefined(); + __func__, "Failed initial canary check for +output_cobhan_buffer"); return env.Undefined(); } if (unlikely(verbose_flag)) { @@ -1136,18 +1066,18 @@ Napi::Value DecryptString(const Napi::Env &env, } if (unlikely(!check_canary_ptr(__func__, logger, input_canary_ptr))) { logger.log_error_and_throw( - __func__, "Failed post-call canary check for input_cobhan_buffer"); - return env.Undefined(); + __func__, "Failed post-call canary check for +input_cobhan_buffer"); return env.Undefined(); } if (unlikely(!check_canary_ptr(__func__, logger, output_canary_ptr))) { logger.log_error_and_throw( - __func__, "Failed post-call canary check for output_cobhan_buffer"); - return env.Undefined(); + __func__, "Failed post-call canary check for +output_cobhan_buffer"); return env.Undefined(); } if (unlikely(result < 0)) { - logger.log_error_and_throw(__func__, AsherahCobhanErrorToString(result)); - return env.Undefined(); + logger.log_error_and_throw(__func__, +AsherahCobhanErrorToString(result)); return env.Undefined(); } Napi::String output; diff --git a/src/asherah.d.ts b/src/asherah.d.ts index 9b00af1..3b10c67 100644 --- a/src/asherah.d.ts +++ b/src/asherah.d.ts @@ -1,43 +1,43 @@ /// export type AsherahConfig = { - /** The name of this service (Required) */ - ServiceName: string; - /** The name of the product that owns this service (Required) */ - ProductID: string; - /** The amount of time a key is considered valid */ - ExpireAfter: number | null; - /** The amount of time before cached keys are considered stale */ - CheckInterval: number | null; - /** Determines the type of metastore to use for persisting keys (Required) { "rdbms", "dynamodb", "memory" } */ - Metastore: string; - /** The database connection string (Required if metastore=rdbms) */ - ConnectionString: string | null; - /** Required for Aurora sessions using write forwarding { "eventual", "global", "session" } */ - ReplicaReadConsistency: string | null; - /** An optional endpoint URL (hostname only or fully qualified URI) (only supported by metastore=dynamodb) */ - DynamoDBEndpoint: string | null; - /** The AWS region for DynamoDB requests (defaults to globally configured region) (only supported by metastore=dynamodb) */ - DynamoDBRegion: string | null; - /** The table name for DynamoDB (only supported by metastore=dynamodb) */ - DynamoDBTableName: string | null; - /** Define the maximum number of sessions to cache (Default 1000) */ - SessionCacheMaxSize: number | null; - /** The amount of time a session will remain cached (Default 2h) */ - SessionCacheDuration: number | null; - /** Configures the master key management service (Default kms) { "aws", "static" } */ - KMS: string | null; - /** Dictionary of REGION: ARN (required if kms=aws) */ - RegionMap: { - [name: string]: string; - } | null; - /** The preferred AWS region (required if kms=aws) */ - PreferredRegion: string | null; - /** Configure the metastore to use regional suffixes (only supported by metastore=dynamodb) */ - EnableRegionSuffix: boolean | null; - /** Enable shared session caching */ - EnableSessionCaching: boolean | null; - /** Enable verbose logging output */ - Verbose: boolean | null; + /** The name of this service (Required) */ + ServiceName: string; + /** The name of the product that owns this service (Required) */ + ProductID: string; + /** The amount of time a key is considered valid */ + ExpireAfter: number | null; + /** The amount of time before cached keys are considered stale */ + CheckInterval: number | null; + /** Determines the type of metastore to use for persisting keys (Required) { "rdbms", "dynamodb", "memory" } */ + Metastore: string; + /** The database connection string (Required if metastore=rdbms) */ + ConnectionString: string | null; + /** Required for Aurora sessions using write forwarding { "eventual", "global", "session" } */ + ReplicaReadConsistency: string | null; + /** An optional endpoint URL (hostname only or fully qualified URI) (only supported by metastore=dynamodb) */ + DynamoDBEndpoint: string | null; + /** The AWS region for DynamoDB requests (defaults to globally configured region) (only supported by metastore=dynamodb) */ + DynamoDBRegion: string | null; + /** The table name for DynamoDB (only supported by metastore=dynamodb) */ + DynamoDBTableName: string | null; + /** Define the maximum number of sessions to cache (Default 1000) */ + SessionCacheMaxSize: number | null; + /** The amount of time a session will remain cached (Default 2h) */ + SessionCacheDuration: number | null; + /** Configures the master key management service (Default kms) { "aws", "static" } */ + KMS: string | null; + /** Dictionary of REGION: ARN (required if kms=aws) */ + RegionMap: { + [name: string]: string; + } | null; + /** The preferred AWS region (required if kms=aws) */ + PreferredRegion: string | null; + /** Configure the metastore to use regional suffixes (only supported by metastore=dynamodb) */ + EnableRegionSuffix: boolean | null; + /** Enable shared session caching */ + EnableSessionCaching: boolean | null; + /** Enable verbose logging output */ + Verbose: boolean | null; }; type LogHookCallback = (level: number, message: string) => void; diff --git a/src/asherah_async_worker.h b/src/asherah_async_worker.h index 1020b35..9d58d80 100644 --- a/src/asherah_async_worker.h +++ b/src/asherah_async_worker.h @@ -4,44 +4,68 @@ #include #include +#include -#define ASHERAH_ASYNC_WORKER(CLASS_NAME, RESULT_TYPE) \ -class CLASS_NAME : public AsherahAsyncWorker { \ -public: \ - CLASS_NAME(Napi::Function& callback) : AsherahAsyncWorker(callback) {} \ - \ -protected: \ - RESULT_TYPE ExecuteTask() override; \ -} +class Asherah; + +#define ASHERAH_ASYNC_WORKER(CLASS_NAME, RESULT_TYPE) \ + class CLASS_NAME : public AsherahAsyncWorker { \ + public: \ + CLASS_NAME() : AsherahAsyncWorker() {} \ + \ + protected: \ + RESULT_TYPE ExecuteTask() override; \ + } template class AsherahAsyncWorker : public Napi::AsyncWorker { public: - AsherahAsyncWorker(Napi::Function& callback) - : Napi::AsyncWorker(callback) {} - - void OnOK() override { - Napi::HandleScope scope(Env()); - Callback().Call({Env().Undefined(), result}); - } + Napi::Promise Promise() { return deferred.Promise(); } - void OnError(Napi::Error const &error) override { - Napi::HandleScope scope(Env()); - Callback().Call({error.Value()}); - } + AsherahAsyncWorker(Napi::Env env, Asherah *instance) + : Napi::AsyncWorker(env), asherah(instance), deferred(env) {} protected: - ResultType result; + Asherah *asherah; + ResultType result; + + virtual ResultType ExecuteTask() = 0; + virtual Napi::Value OnOKTask() = 0; + virtual Napi::Value OnErrorTask(Napi::Error const &error) { + return error.Value(); + } + +private: + Napi::Promise::Deferred deferred; + + void Execute() override final { + try { + result = ExecuteTask(); + } catch (const std::exception &ex) { + SetError(ex.what()); + } + } - virtual ResultType ExecuteTask() = 0; + void OnOK() override final { + Napi::HandleScope scope(Env()); + try { + auto value = OnOKTask(); + deferred.Resolve(value); + } catch (const std::exception &e) { + deferred.Reject(Napi::Error::New(Env(), e.what()).Value()); + } + } - void Execute() override { - try { - result = ExecuteTask(); - } catch (const std::exception& ex) { - SetError(ex.what()); - } + void OnError(Napi::Error const &error) override final { + Napi::HandleScope scope(Env()); + try { + std::cerr << "AsherahAsyncWorker.OnError called" << std::endl + << std::flush; + deferred.Reject(OnErrorTask(error)); + } catch (const std::exception &e) { + deferred.Reject(Napi::Error::New(Env(), e.what()).Value()); } + } }; -#endif // ASHERAH_ASYNC_WORKER_H \ No newline at end of file +#endif // ASHERAH_ASYNC_WORKER_H diff --git a/src/cobhan_buffer.h b/src/cobhan_buffer.h index 1211248..ebf37cc 100644 --- a/src/cobhan_buffer.h +++ b/src/cobhan_buffer.h @@ -2,243 +2,310 @@ #define ASHERAH_NODE_COBHAN_BUFFER_H #include -#include // for std::memcpy -#include // for std::cerr +#include // for std::memcpy +#include // for std::cerr +#include // for std::numeric_limits #include // for std::runtime_error, std::invalid_argument -#include // for std::numeric_limits class CobhanBuffer { public: - // Used for requesting a new heap-based buffer allocation that can handle data_len_bytes of data - explicit CobhanBuffer(size_t data_len_bytes) { - std::cerr << "CobhanBuffer: Constructor data_len_bytes called with " << data_len_bytes << std::endl << std::flush; - if (data_len_bytes > max_int32_size) { - std::cerr << "Requested data length exceeds maximum allowable size." << std::endl << std::flush; - throw std::invalid_argument("Requested data length exceeds maximum allowable size."); - } - std::cerr << "CobhanBuffer: Constructor data_len_bytes calling DataSizeToAllocationSize" << std::endl << std::flush; - allocation_size = DataSizeToAllocationSize(data_len_bytes); - std::cerr << "CobhanBuffer: Constructor data_len_bytes calculated allocation size " << allocation_size << std::endl << std::flush; - - cbuffer = new char[allocation_size]; - std::cerr << "CobhanBuffer: Constructor data_len_bytes Allocated " << allocation_size << " at " << static_cast(cbuffer) << std::endl << std::flush; - ownership = true; - initialize(data_len_bytes); + // Used for requesting a new heap-based buffer allocation that can handle + // data_len_bytes of data + explicit CobhanBuffer(size_t data_len_bytes) { + std::cerr << "CobhanBuffer: Constructor data_len_bytes called with " + << data_len_bytes << std::endl + << std::flush; + if (data_len_bytes > max_int32_size) { + std::cerr << "Requested data length exceeds maximum allowable size." + << std::endl + << std::flush; + throw std::invalid_argument( + "Requested data length exceeds maximum allowable size."); } + std::cerr << "CobhanBuffer: Constructor data_len_bytes calling " + "DataSizeToAllocationSize" + << std::endl + << std::flush; + allocation_size = DataSizeToAllocationSize(data_len_bytes); + std::cerr << "CobhanBuffer: Constructor data_len_bytes calculated " + "allocation size " + << allocation_size << std::endl + << std::flush; - // Used for passing a stack-based buffer allocation that hasn't been initialized yet - explicit CobhanBuffer(char* cbuffer, size_t allocation_size) - : cbuffer(cbuffer), allocation_size(allocation_size), ownership(false) { - if (allocation_size > max_int32_size) { - std::cerr << "Allocation size exceeds maximum allowable size." << std::endl << std::flush; - throw std::invalid_argument("Allocation size exceeds maximum allowable size."); - } - initialize(); - } + cbuffer = new char[allocation_size]; + std::cerr << "CobhanBuffer: Constructor data_len_bytes Allocated " + << allocation_size << " at " << static_cast(cbuffer) + << std::endl + << std::flush; + ownership = true; + initialize(data_len_bytes); + } - // Move constructor - CobhanBuffer(CobhanBuffer&& other) noexcept { - moveFrom(std::move(other)); + // Used for passing a stack-based buffer allocation that hasn't been + // initialized yet + explicit CobhanBuffer(char *cbuffer, size_t allocation_size) + : cbuffer(cbuffer), allocation_size(allocation_size), ownership(false) { + if (allocation_size > max_int32_size) { + std::cerr << "Allocation size exceeds maximum allowable size." + << std::endl + << std::flush; + throw std::invalid_argument( + "Allocation size exceeds maximum allowable size."); } + initialize(); + } - // Move assignment operator - CobhanBuffer& operator=(CobhanBuffer&& other) noexcept { - if (this != &other) { - cleanup(); - moveFrom(std::move(other)); - } - return *this; - } + // Move constructor + CobhanBuffer(CobhanBuffer &&other) noexcept { moveFrom(std::move(other)); } - // Implicit cast to void* - operator void*() const { // NOLINT(*-explicit-constructor) - return static_cast(cbuffer); + // Move assignment operator + CobhanBuffer &operator=(CobhanBuffer &&other) noexcept { + if (this != &other) { + cleanup(); + moveFrom(std::move(other)); } + return *this; + } - char* get_data_ptr() const { - return data_ptr; - } + // Implicit cast to void* + operator void *() const { // NOLINT(*-explicit-constructor) + return static_cast(cbuffer); + } - size_t get_data_len_bytes() const { - return *data_len_ptr; - } + char *get_data_ptr() const { return data_ptr; } - ~CobhanBuffer() { - if (!verify_canaries()) { - std::cerr << "asherah-node: Memory corruption detected: Canary values are corrupted." << std::endl << std::flush; - std::terminate(); - } - cleanup(); - } + size_t get_data_len_bytes() const { return *data_len_ptr; } - void DebugPrintStdErr() const { - auto debug_string = std::string(get_data_ptr(), get_data_len_bytes()); - std::cerr << "CobhanBuffer { " << std::endl - << "data_len_bytes=" << get_data_len_bytes() << std::endl - << ", allocation_size=" << get_allocation_size() << std::endl - << ", data_ptr=" << static_cast(get_data_ptr()) << std::endl - << ", canary1=" << static_cast(canary1_ptr) << std::endl - << ", canary2=" << static_cast(canary2_ptr) << std::endl - << ", ownership=" << ownership << std::endl - << ", string=[" << debug_string << "]" << std::endl - << ", string_length=" << debug_string.length() << std::endl - << "}" << std::endl << std::flush; + ~CobhanBuffer() { + if (!verify_canaries()) { + std::cerr << "asherah-node: Memory corruption detected: Canary values " + "are corrupted." + << std::endl + << std::flush; + std::terminate(); } + cleanup(); + } + + void DebugPrintStdErr() const { + auto debug_string = std::string(get_data_ptr(), get_data_len_bytes()); + std::cerr << "CobhanBuffer { " << std::endl + << "data_len_bytes=" << get_data_len_bytes() << std::endl + << ", allocation_size=" << get_allocation_size() << std::endl + << ", data_ptr=" << static_cast(get_data_ptr()) + << std::endl + << ", canary1=" << static_cast(canary1_ptr) << std::endl + << ", canary2=" << static_cast(canary2_ptr) << std::endl + << ", ownership=" << ownership << std::endl + << ", string=[" << debug_string << "]" << std::endl + << ", string_length=" << debug_string.length() << std::endl + << "}" << std::endl + << std::flush; + } - static size_t DataSizeToAllocationSize(size_t data_len_bytes) { - std::cerr << "DataSizeToAllocationSize called: data_len_bytes=" << data_len_bytes << std::endl << std::flush; - size_t allocation = data_len_bytes + cobhan_header_size_bytes + - canary_size_bytes // Add space for canary value - + safety_padding_bytes; // Add safety padding if configured - if (allocation > max_int32_size) { - std::cerr << "DataSizeToAllocationSize: Calculated allocation size exceeds maximum allowable size." << std::endl << std::flush; - throw std::invalid_argument("Calculated allocation size exceeds maximum allowable size."); - } - std::cerr << "DataSizeToAllocationSize returning: " << allocation << std::endl << std::flush; - return allocation; + static size_t DataSizeToAllocationSize(size_t data_len_bytes) { + std::cerr << "DataSizeToAllocationSize called: data_len_bytes=" + << data_len_bytes << std::endl + << std::flush; + size_t allocation = + data_len_bytes + cobhan_header_size_bytes + + canary_size_bytes // Add space for canary value + + safety_padding_bytes; // Add safety padding if configured + if (allocation > max_int32_size) { + std::cerr << "DataSizeToAllocationSize: Calculated allocation size " + "exceeds maximum allowable size." + << std::endl + << std::flush; + throw std::invalid_argument( + "Calculated allocation size exceeds maximum allowable size."); } + std::cerr << "DataSizeToAllocationSize returning: " << allocation + << std::endl + << std::flush; + return allocation; + } - static size_t AllocationSizeToMaxDataSize(size_t allocation_len_bytes) { - std::cerr << "AllocationSizeToMaxDataSize called: allocation_len_bytes=" << allocation_len_bytes << std::endl << std::flush; - size_t data_len_bytes = allocation_len_bytes - cobhan_header_size_bytes - canary_size_bytes - safety_padding_bytes; - if (data_len_bytes > max_int32_size) { - std::cerr << "AllocationSizeToMaxDataSize: Calculated data size exceeds maximum allowable size." << std::endl << std::flush; - throw std::invalid_argument("Calculated data size exceeds maximum allowable size."); - } - std::cerr << "AllocationSizeToMaxDataSize returning: " << data_len_bytes << std::endl << std::flush; - return data_len_bytes; + static size_t AllocationSizeToMaxDataSize(size_t allocation_len_bytes) { + std::cerr << "AllocationSizeToMaxDataSize called: allocation_len_bytes=" + << allocation_len_bytes << std::endl + << std::flush; + size_t data_len_bytes = allocation_len_bytes - cobhan_header_size_bytes - + canary_size_bytes - safety_padding_bytes; + if (data_len_bytes > max_int32_size) { + std::cerr << "AllocationSizeToMaxDataSize: Calculated data size exceeds " + "maximum allowable size." + << std::endl + << std::flush; + throw std::invalid_argument( + "Calculated data size exceeds maximum allowable size."); } + std::cerr << "AllocationSizeToMaxDataSize returning: " << data_len_bytes + << std::endl + << std::flush; + return data_len_bytes; + } protected: - bool verify_canaries() const { - bool valid = true; - if (*canary1_ptr != 0) { - std::cerr << "Canary 1 corrupted! Expected: 0, Found: " << *canary1_ptr << '\n'; - valid = false; - } - if (*canary2_ptr != canary_constant) { - std::cerr << "Canary 2 corrupted! Expected: 0xdeadbeef, Found: " << *canary2_ptr << '\n'; - valid = false; - } - return valid; + bool verify_canaries() const { + bool valid = true; + if (*canary1_ptr != 0) { + std::cerr << "Canary 1 corrupted! Expected: 0, Found: " << *canary1_ptr + << '\n'; + valid = false; } - - size_t get_allocation_size() const { - return allocation_size; + if (*canary2_ptr != canary_constant) { + std::cerr << "Canary 2 corrupted! Expected: 0xdeadbeef, Found: " + << *canary2_ptr << '\n'; + valid = false; } + return valid; + } + + size_t get_allocation_size() const { return allocation_size; } - void set_data_len_bytes(size_t data_len_bytes) { - if (data_len_bytes > max_int32_size) { - throw std::invalid_argument("Requested data length exceeds maximum allowable size."); - } - if (data_len_bytes > allocation_size) { - throw std::invalid_argument("Requested data length exceeds allocation size."); - } - *data_len_ptr = static_cast(data_len_bytes); + void set_data_len_bytes(size_t data_len_bytes) { + if (data_len_bytes > max_int32_size) { + throw std::invalid_argument( + "Requested data length exceeds maximum allowable size."); + } + if (data_len_bytes > allocation_size) { + throw std::invalid_argument( + "Requested data length exceeds allocation size."); } + *data_len_ptr = static_cast(data_len_bytes); + } private: - //Assumes cbuffer and allocation_size are already set - void initialize(size_t data_len_bytes = 0) { - // Save pointer to data - data_ptr = cbuffer + cobhan_header_size_bytes; - // First int32_t is the data length - data_len_ptr = reinterpret_cast(cbuffer); - // Second int32_t is reserved for future use - auto reserved_ptr = reinterpret_cast(cbuffer + sizeof(int32_t)); - // First canary value is an int32_t 0 which gives us four NULLs - canary1_ptr = reinterpret_cast(cbuffer + allocation_size - canary_size_bytes); - // Second canary value is an int32_t 0xdeadbeef - canary2_ptr = canary1_ptr + 1; - - // Calculate the data length - if(data_len_bytes == 0) { - data_len_bytes = AllocationSizeToMaxDataSize(allocation_size); - } - - if (data_len_bytes > max_int32_size) { - throw std::invalid_argument("Data length exceeds maximum allowable size."); - } - - // Write Cobhan header values - std::cerr << "asherah-node: Setting data length to " << data_len_bytes << std::endl << std::flush; - *data_len_ptr = static_cast(data_len_bytes); - - // Reserved for future use - *reserved_ptr = 0; - - // Write canary values - *canary1_ptr = 0; - *canary2_ptr = canary_constant; + // Assumes cbuffer and allocation_size are already set + void initialize(size_t data_len_bytes = 0) { + // Save pointer to data + data_ptr = cbuffer + cobhan_header_size_bytes; + // First int32_t is the data length + data_len_ptr = reinterpret_cast(cbuffer); + // Second int32_t is reserved for future use + auto reserved_ptr = reinterpret_cast(cbuffer + sizeof(int32_t)); + // First canary value is an int32_t 0 which gives us four NULLs + canary1_ptr = reinterpret_cast(cbuffer + allocation_size - + canary_size_bytes); + // Second canary value is an int32_t 0xdeadbeef + canary2_ptr = canary1_ptr + 1; + + // Calculate the data length + if (data_len_bytes == 0) { + data_len_bytes = AllocationSizeToMaxDataSize(allocation_size); } - void moveFrom(CobhanBuffer&& other) { - if (!other.verify_canaries()) { - std::cerr << "Memory corruption detected in the source buffer: Canary values are corrupted." << std::endl << std::flush; - throw std::runtime_error("Memory corruption detected in the source buffer: Canary values are corrupted."); - } - - if (other.ownership) { - std::cerr << "asherah-node: Moving CobhanBuffer with ownership" << std::endl << std::flush; - // Transfer ownership of the existing buffer - cbuffer = other.cbuffer; - allocation_size = other.allocation_size; - ownership = true; - data_ptr = other.data_ptr; - data_len_ptr = other.data_len_ptr; - canary1_ptr = other.canary1_ptr; - canary2_ptr = other.canary2_ptr; - - std::cerr << "asherah-node: Resetting other object" << std::endl << std::flush; - // Reset the other object to prevent it from deallocating the buffer - other.cbuffer = nullptr; - other.allocation_size = 0; - other.ownership = false; - } else { - std::cerr << "asherah-node: Moving CobhanBuffer without ownership" << std::endl << std::flush; - // Allocate a new buffer and copy the contents - allocation_size = other.allocation_size; - if (allocation_size > max_int32_size) { - throw std::invalid_argument("Allocation size exceeds maximum allowable size."); - } - - std::cerr << "asherah-node: Allocating new buffer for move" << std::endl << std::flush; - cbuffer = new char[allocation_size]; - std::cerr << "asherah-node: Move without ownership Allocated " << allocation_size << " at " << static_cast(cbuffer) << std::endl << std::flush; - - std::cerr << "asherah-node: Copying data for move" << std::endl << std::flush; - std::memcpy(cbuffer, other.cbuffer, allocation_size); - std::cerr << "asherah-node: Done copying data for move" << std::endl << std::flush; - ownership = true; - std::cerr << "asherah-node: Configuring new buffer for move" << std::endl << std::flush; - initialize(*other.data_len_ptr); - std::cerr << "asherah-node: Done configuring new buffer for move" << std::endl << std::flush; - } + if (data_len_bytes > max_int32_size) { + throw std::invalid_argument( + "Data length exceeds maximum allowable size."); } - void cleanup() { - if (ownership) { - std::cerr << "asherah-node: Freeing owned CobhanBuffer " << static_cast(cbuffer) << std::endl << std::flush; - delete[] cbuffer; - } - std::cerr << "asherah-node: CobhanBuffer clean up" << std::endl << std::flush; - cbuffer = nullptr; - allocation_size = 0; + // Write Cobhan header values + std::cerr << "asherah-node: Setting data length to " << data_len_bytes + << std::endl + << std::flush; + *data_len_ptr = static_cast(data_len_bytes); + + // Reserved for future use + *reserved_ptr = 0; + + // Write canary values + *canary1_ptr = 0; + *canary2_ptr = canary_constant; + } + + void moveFrom(CobhanBuffer &&other) { + if (!other.verify_canaries()) { + std::cerr << "Memory corruption detected in the source buffer: Canary " + "values are corrupted." + << std::endl + << std::flush; + throw std::runtime_error("Memory corruption detected in the source " + "buffer: Canary values are corrupted."); + } + + if (other.ownership) { + std::cerr << "asherah-node: Moving CobhanBuffer with ownership" + << std::endl + << std::flush; + // Transfer ownership of the existing buffer + cbuffer = other.cbuffer; + allocation_size = other.allocation_size; + ownership = true; + data_ptr = other.data_ptr; + data_len_ptr = other.data_len_ptr; + canary1_ptr = other.canary1_ptr; + canary2_ptr = other.canary2_ptr; + + std::cerr << "asherah-node: Resetting other object" << std::endl + << std::flush; + // Reset the other object to prevent it from deallocating the buffer + other.cbuffer = nullptr; + other.allocation_size = 0; + other.ownership = false; + } else { + std::cerr << "asherah-node: Moving CobhanBuffer without ownership" + << std::endl + << std::flush; + // Allocate a new buffer and copy the contents + allocation_size = other.allocation_size; + if (allocation_size > max_int32_size) { + throw std::invalid_argument( + "Allocation size exceeds maximum allowable size."); + } + + std::cerr << "asherah-node: Allocating new buffer for move" << std::endl + << std::flush; + cbuffer = new char[allocation_size]; + std::cerr << "asherah-node: Move without ownership Allocated " + << allocation_size << " at " << static_cast(cbuffer) + << std::endl + << std::flush; + + std::cerr << "asherah-node: Copying data for move" << std::endl + << std::flush; + std::memcpy(cbuffer, other.cbuffer, allocation_size); + std::cerr << "asherah-node: Done copying data for move" << std::endl + << std::flush; + ownership = true; + std::cerr << "asherah-node: Configuring new buffer for move" << std::endl + << std::flush; + initialize(*other.data_len_ptr); + std::cerr << "asherah-node: Done configuring new buffer for move" + << std::endl + << std::flush; } + } + + void cleanup() { + if (ownership) { + std::cerr << "asherah-node: Freeing owned CobhanBuffer " + << static_cast(cbuffer) << std::endl + << std::flush; + delete[] cbuffer; + } + std::cerr << "asherah-node: CobhanBuffer clean up" << std::endl + << std::flush; + cbuffer = nullptr; + allocation_size = 0; + } + + char *cbuffer = nullptr; + size_t allocation_size = 0; + bool ownership = false; + int32_t *data_len_ptr = nullptr; + char *data_ptr = nullptr; + int32_t *canary1_ptr = nullptr; + int32_t *canary2_ptr = nullptr; - char* cbuffer = nullptr; - size_t allocation_size = 0; - bool ownership = false; - int32_t* data_len_ptr = nullptr; - char* data_ptr = nullptr; - int32_t* canary1_ptr = nullptr; - int32_t* canary2_ptr = nullptr; - - static constexpr int32_t canary_constant = static_cast(0xdeadbeef); - static constexpr size_t cobhan_header_size_bytes = sizeof(int32_t) * 2; // 2x int32_t headers - static constexpr size_t canary_size_bytes = sizeof(int32_t) * 2; // Two int32_t canaries - static constexpr size_t safety_padding_bytes = 8; - static constexpr size_t max_int32_size = static_cast(std::numeric_limits::max()); + static constexpr int32_t canary_constant = static_cast(0xdeadbeef); + static constexpr size_t cobhan_header_size_bytes = + sizeof(int32_t) * 2; // 2x int32_t headers + static constexpr size_t canary_size_bytes = + sizeof(int32_t) * 2; // Two int32_t canaries + static constexpr size_t safety_padding_bytes = 8; + static constexpr size_t max_int32_size = + static_cast(std::numeric_limits::max()); }; -#endif //ASHERAH_NODE_COBHAN_BUFFER_H +#endif // ASHERAH_NODE_COBHAN_BUFFER_H diff --git a/src/cobhan_buffer_napi.h b/src/cobhan_buffer_napi.h index 89a5ee5..3a6518c 100644 --- a/src/cobhan_buffer_napi.h +++ b/src/cobhan_buffer_napi.h @@ -8,149 +8,188 @@ class CobhanBufferNapi : public CobhanBuffer { public: - // Constructor from a Napi::String - CobhanBufferNapi(const Napi::Env& env, const Napi::String& napiString) - : CobhanBuffer(NapiUtils::GetUtf8StringLength(env, napiString) + 1) { // Add one for possible NULL delimiter due to Node string functions - copy_from_string(env, napiString); + // Constructor from a Napi::String + CobhanBufferNapi(const Napi::Env &env, const Napi::String &napiString) + : CobhanBuffer(NapiUtils::GetUtf8StringLength(env, napiString) + + 1) { // Add one for possible NULL delimiter due to Node + // string functions + copy_from_string(env, napiString); + } + + // Constructor from Napi::Buffer + explicit CobhanBufferNapi(const Napi::Env &env, + const Napi::Buffer &napiBuffer) + : CobhanBuffer(napiBuffer.ByteLength()) { + std::memcpy(get_data_ptr(), napiBuffer.Data(), napiBuffer.ByteLength()); + } + + // Constructor from Napi::Value + explicit CobhanBufferNapi(const Napi::Env &env, const Napi::Value &value) + : CobhanBuffer(ValueToDataSize(env, value)) { + if (value.IsString()) { + copy_from_string(env, value.As()); + } else if (value.IsBuffer()) { + Napi::Buffer napiBuffer = + value.As>(); + std::memcpy(get_data_ptr(), napiBuffer.Data(), napiBuffer.Length()); + } else { + throw std::invalid_argument("Expected a Napi::String or " + "Napi::Buffer as the value."); } - - // Constructor from Napi::Buffer - explicit CobhanBufferNapi(const Napi::Env& env, const Napi::Buffer& napiBuffer) - : CobhanBuffer(napiBuffer.ByteLength()) { - std::memcpy(get_data_ptr(), napiBuffer.Data(), napiBuffer.ByteLength()); + } + + // Constructor from a Napi::String to an externally allocated buffer + CobhanBufferNapi(const Napi::Env &env, const Napi::String &napiString, + char *cbuffer, size_t allocation_size) + : CobhanBuffer(cbuffer, allocation_size) { + std::cerr << "CobhanBufferNapi called: cbuffer=" + << static_cast(cbuffer) + << " allocation_size=" << allocation_size << std::endl + << std::flush; + + std::cerr << "CobhanBufferNapi: copying from string" << std::endl + << std::flush; + copy_from_string(env, napiString); + std::cerr << "CobhanBufferNapi: done" << std::endl << std::flush; + } + + // Constructor from size_t representing data length in bytes (not allocation + // size) + explicit CobhanBufferNapi(size_t data_len_bytes) + : CobhanBuffer(data_len_bytes) {} + + // Constructor from externally allocated char* and size_t representing + // allocation size in bytes + explicit CobhanBufferNapi(char *cbuffer, size_t allocation_size) + : CobhanBuffer(cbuffer, allocation_size) {} + + // Move constructor + CobhanBufferNapi(CobhanBufferNapi &&other) noexcept + : CobhanBuffer(std::move(other)) {} + + // Move assignment operator + CobhanBufferNapi &operator=(CobhanBufferNapi &&other) noexcept { + if (this != &other) { + CobhanBuffer::operator=(std::move(other)); } - - // Constructor from Napi::Value - explicit CobhanBufferNapi(const Napi::Env& env, const Napi::Value& value) - : CobhanBuffer(ValueToDataSize(env, value)) { - if (value.IsString()) { - copy_from_string(env, value.As()); - } else if (value.IsBuffer()) { - Napi::Buffer napiBuffer = value.As>(); - std::memcpy(get_data_ptr(), napiBuffer.Data(), napiBuffer.Length()); - } else { - throw std::invalid_argument("Expected a Napi::String or Napi::Buffer as the value."); - } + return *this; + } + + // Returns a Napi::String from the buffer using napi_create_string_utf8 + Napi::String ToString(const Napi::Env &env) const { + napi_value napiStr; + napi_status status = napi_create_string_utf8( + env, get_data_ptr(), get_data_len_bytes(), &napiStr); + + if (status != napi_ok) { + napi_throw_error(env, nullptr, + "Failed to create Napi::String from CobhanBuffer"); + return {}; } - // Constructor from a Napi::String to an externally allocated buffer - CobhanBufferNapi(const Napi::Env& env, const Napi::String& napiString, char* cbuffer, size_t allocation_size) - : CobhanBuffer(cbuffer, allocation_size) { - std::cerr << "CobhanBufferNapi called: cbuffer=" << static_cast(cbuffer) << " allocation_size=" << allocation_size << std::endl << std::flush; - /* - size_t str_len = NapiUtils::GetUtf8StringLength(env, napiString); - std::cerr << "CobhanBufferNapi: str_len=" << str_len << std::endl << std::flush; - size_t required_allocation_size = CobhanBuffer::DataSizeToAllocationSize(str_len); - std::cerr << "CobhanBufferNapi: required_allocation_size=" << required_allocation_size << std::endl << std::flush; - if (required_allocation_size > allocation_size) { - std::cerr << "Buffer allocation size is insufficient to hold the Napi::String." << std::endl << std::flush; - throw std::invalid_argument("Buffer allocation size is insufficient to hold the Napi::String."); - } - */ - std::cerr << "CobhanBufferNapi: copying from string" << std::endl << std::flush; - copy_from_string(env, napiString); - std::cerr << "CobhanBufferNapi: done" << std::endl << std::flush; - } - - // Constructor from size_t representing data length in bytes (not allocation size) - explicit CobhanBufferNapi(size_t data_len_bytes) : CobhanBuffer(data_len_bytes) {} - - // Constructor from externally allocated char* and size_t representing allocation size in bytes - explicit CobhanBufferNapi(char* cbuffer, size_t allocation_size) : CobhanBuffer(cbuffer, allocation_size) {} - - // Move constructor - CobhanBufferNapi(CobhanBufferNapi&& other) noexcept : CobhanBuffer(std::move(other)) {} - - // Move assignment operator - CobhanBufferNapi& operator=(CobhanBufferNapi&& other) noexcept { - if (this != &other) { - CobhanBuffer::operator=(std::move(other)); - } - return *this; + return {env, napiStr}; + } + + // Returns a Napi::Buffer from the buffer + Napi::Buffer ToBuffer(const Napi::Env &env) const { + std::cerr << "ToBuffer called" << std::endl << std::flush; + auto buffer = Napi::Buffer::Copy( + env, reinterpret_cast(get_data_ptr()), + get_data_len_bytes()); + std::cerr << "ToBuffer done" << std::endl << std::flush; + return buffer; + } + + // Public method to calculate the required allocation size for a Napi::String + static size_t StringToAllocationSize(const Napi::Env &env, + const Napi::String &napiString) { + size_t str_len = NapiUtils::GetUtf8StringLength(env, napiString); + return DataSizeToAllocationSize(str_len) + + 1; // Add one for possible NULL delimiter due to Node string + // functions + } + + // Public method to calculate the required allocation size for a + // Napi::Buffer + static size_t + BufferToAllocationSize(const Napi::Env &, + const Napi::Buffer &napiBuffer) { + return DataSizeToAllocationSize(napiBuffer.Length()); + } + + // Public method to calculate the required allocation size for a Napi::Value + // (either Napi::String or Napi::Buffer) + static size_t ValueToAllocationSize(const Napi::Env &env, + const Napi::Value &value) { + if (value.IsString()) { + return StringToAllocationSize(env, value.As()); + } else if (value.IsBuffer()) { + return BufferToAllocationSize(env, + value.As>()); + } else { + throw std::invalid_argument("Expected a Napi::String or " + "Napi::Buffer as the value."); } - - // Returns a Napi::String from the buffer using napi_create_string_utf8 - Napi::String ToString(const Napi::Env& env) const { - napi_value napiStr; - napi_status status = napi_create_string_utf8( - env, get_data_ptr(), get_data_len_bytes(), &napiStr); - - if (status != napi_ok) { - napi_throw_error(env, nullptr, "Failed to create Napi::String from CobhanBuffer"); - return {}; - } - - return { env, napiStr }; + } + + static size_t ValueToDataSize(const Napi::Env &env, + const Napi::Value &value) { + if (value.IsString()) { + return NapiUtils::GetUtf8StringLength(env, value.As()) + 1; + } else if (value.IsBuffer()) { + return value.As>().ByteLength(); + } else { + throw std::invalid_argument("Expected a Napi::String or " + "Napi::Buffer as the value."); } + } - // Returns a Napi::Buffer from the buffer - Napi::Buffer ToBuffer(const Napi::Env& env) const { - std::cerr << "ToBuffer called" << std::endl << std::flush; - auto buffer = Napi::Buffer::Copy(env, reinterpret_cast(get_data_ptr()), get_data_len_bytes()); - std::cerr << "ToBuffer done" << std::endl << std::flush; - return buffer; - } - - // Public method to calculate the required allocation size for a Napi::String - static size_t StringToAllocationSize(const Napi::Env& env, const Napi::String& napiString) { - size_t str_len = NapiUtils::GetUtf8StringLength(env, napiString); - return DataSizeToAllocationSize(str_len) + 1; // Add one for possible NULL delimiter due to Node string functions - } - - // Public method to calculate the required allocation size for a Napi::Buffer - static size_t BufferToAllocationSize(const Napi::Env&, const Napi::Buffer& napiBuffer) { - return DataSizeToAllocationSize(napiBuffer.Length()); - } - - // Public method to calculate the required allocation size for a Napi::Value (either Napi::String or Napi::Buffer) - static size_t ValueToAllocationSize(const Napi::Env& env, const Napi::Value& value) { - if (value.IsString()) { - return StringToAllocationSize(env, value.As()); - } else if (value.IsBuffer()) { - return BufferToAllocationSize(env, value.As>()); - } else { - throw std::invalid_argument("Expected a Napi::String or Napi::Buffer as the value."); - } +private: + void copy_from_string(const Napi::Env &env, const Napi::String &napiString) { + std::cerr << "copy_from_string: copying from string" << std::endl + << std::flush; + + std::cerr << "copy_from_string: getting string length" << std::endl + << std::flush; + size_t str_len = NapiUtils::GetUtf8StringLength(env, napiString); + std::cerr << "copy_from_string: str_len=" << str_len << std::endl + << std::flush; + + std::cerr << "copy_from_string: calculating allocation size" << std::endl + << std::flush; + size_t allocation_size = + CobhanBuffer::DataSizeToAllocationSize(str_len) + 1; + std::cerr << "copy_from_string: allocation_size=" << allocation_size + << std::endl + << std::flush; + + if (allocation_size > get_allocation_size()) { + std::cerr << "copy_from_string: Buffer allocation size is insufficient " + "to hold the Napi::String." + << std::endl + << std::flush; + throw std::invalid_argument( + "Buffer allocation size is insufficient to hold the Napi::String."); } - static size_t ValueToDataSize(const Napi::Env& env, const Napi::Value& value) { - if (value.IsString()) { - return NapiUtils::GetUtf8StringLength(env, value.As()) + 1; - } else if (value.IsBuffer()) { - return value.As>().ByteLength(); - } else { - throw std::invalid_argument("Expected a Napi::String or Napi::Buffer as the value."); - } + size_t bytes_written; + napi_status status = napi_get_value_string_utf8( + env, napiString, get_data_ptr(), str_len + 1, &bytes_written); + if (status != napi_ok || bytes_written != str_len) { + std::cerr << "copy_from_string: failed to copy str_len=" << str_len + << ", bytes_written=" << bytes_written << std::endl + << std::flush; + throw std::runtime_error( + "Failed to copy Napi::String into CobhanBuffer. Status: " + + std::to_string(status) + + ", Bytes Written: " + std::to_string(bytes_written)); } -private: - void copy_from_string(const Napi::Env& env, const Napi::String& napiString) { - std::cerr << "copy_from_string: copying from string" << std::endl << std::flush; - - std::cerr << "copy_from_string: getting string length" << std::endl << std::flush; - size_t str_len = NapiUtils::GetUtf8StringLength(env, napiString); - std::cerr << "copy_from_string: str_len=" << str_len << std::endl << std::flush; - - std::cerr << "copy_from_string: calculating allocation size" << std::endl << std::flush; - size_t allocation_size = CobhanBuffer::DataSizeToAllocationSize(str_len) + 1; - std::cerr << "copy_from_string: allocation_size=" << allocation_size << std::endl << std::flush; - - if (allocation_size > get_allocation_size()) { - std::cerr << "copy_from_string: Buffer allocation size is insufficient to hold the Napi::String." << std::endl << std::flush; - throw std::invalid_argument("Buffer allocation size is insufficient to hold the Napi::String."); - } - - size_t bytes_written; - napi_status status = napi_get_value_string_utf8(env, napiString, get_data_ptr(), str_len + 1, &bytes_written); - if (status != napi_ok || bytes_written != str_len) { - std::cerr << "copy_from_string: failed to copy str_len=" << str_len << ", bytes_written=" << bytes_written << std::endl << std::flush; - throw std::runtime_error("Failed to copy Napi::String into CobhanBuffer. Status: " + - std::to_string(status) + ", Bytes Written: " + std::to_string(bytes_written)); - } - - std::cerr << "copy_from_string: setting new data size to " << str_len << std::endl << std::flush; - set_data_len_bytes(str_len); - } + std::cerr << "copy_from_string: setting new data size to " << str_len + << std::endl + << std::flush; + set_data_len_bytes(str_len); + } }; -#endif //COBHAN_BUFFER_NAPI_H +#endif // COBHAN_BUFFER_NAPI_H diff --git a/src/logging.cc b/src/logging.cc index cde9f6e..81dce42 100644 --- a/src/logging.cc +++ b/src/logging.cc @@ -10,9 +10,9 @@ Logger::Logger(Napi::Function new_log_hook) { } Logger::~Logger() { - auto old_log_hook = std::exchange(log_hook, Napi::FunctionReference()); + auto old_log_hook = std::exchange(log_hook, Napi::FunctionReference()); if (!old_log_hook.IsEmpty()) { - old_log_hook.Unref(); + old_log_hook.Unref(); } } @@ -22,7 +22,7 @@ void Logger::set_log_hook(Napi::Function new_log_hook) { } auto old_log_hook = std::exchange(log_hook, Napi::Persistent(new_log_hook)); if (!old_log_hook.IsEmpty()) { - old_log_hook.Unref(); + old_log_hook.Unref(); } } @@ -44,7 +44,8 @@ void Logger::debug_log(const char *function_name, const char *message) const { } } -void Logger::debug_log(const char *function_name, const std::string &message) const { +void Logger::debug_log(const char *function_name, + const std::string &message) const { if (unlikely(verbose_flag)) { if (unlikely(log_hook.IsEmpty())) { stderr_debug_log(function_name, message); @@ -101,9 +102,8 @@ void Logger::debug_log_new(const char *function_name, const char *variable_name, void Logger::debug_log_copy_buffer(const char *function_name, const char *buffer, size_t length) const { if (unlikely(verbose_flag)) { - debug_log(function_name, "Copying " + std::to_string(length) + " bytes to " + - format_ptr(buffer) + " - " + - format_ptr(buffer + length)); + debug_log(function_name, "Copying " + std::to_string(length) + " bytes to " ++ format_ptr(buffer) + " - " + format_ptr(buffer + length)); } } @@ -121,7 +121,7 @@ void Logger::error_log(const char *function_name, const char *message) const { if (unlikely(log_hook.IsEmpty())) { stderr_error_log(function_name, message); } else { - stderr_error_log(function_name, message); + stderr_error_log(function_name, message); Napi::Env env = log_hook.Env(); Napi::HandleScope scope(env); Napi::Function log_hook_function = log_hook.Value(); @@ -133,20 +133,21 @@ void Logger::error_log(const char *function_name, const char *message) const { } } -void Logger::error_log(const char *function_name, const std::string &message) const { +void Logger::error_log(const char *function_name, + const std::string &message) const { - if (unlikely(log_hook.IsEmpty())) { - stderr_error_log(function_name, message); - } else { - stderr_error_log(function_name, message); - Napi::Env env = log_hook.Env(); - Napi::HandleScope scope(env); - Napi::Function log_hook_function = log_hook.Value(); - log_hook_function.Call( - {Napi::Number::New(env, posix_log_level_error), - Napi::String::New(env, asherah_node_prefix + function_name + ": " + - message)}); - } + if (unlikely(log_hook.IsEmpty())) { + stderr_error_log(function_name, message); + } else { + stderr_error_log(function_name, message); + Napi::Env env = log_hook.Env(); + Napi::HandleScope scope(env); + Napi::Function log_hook_function = log_hook.Value(); + log_hook_function.Call( + {Napi::Number::New(env, posix_log_level_error), + Napi::String::New(env, asherah_node_prefix + function_name + ": " + + message)}); + } } __attribute__((always_inline)) inline void @@ -159,7 +160,8 @@ Logger::stderr_debug_log(const char *function_name, const char *message) const { } __attribute__((always_inline)) inline void -Logger::stderr_debug_log(const char *function_name, const std::string &message) const { +Logger::stderr_debug_log(const char *function_name, + const std::string &message) const { if (unlikely(verbose_flag)) { std::cerr << "asherah-node: [DEBUG] " << function_name << ": " << message << std::endl @@ -167,9 +169,8 @@ Logger::stderr_debug_log(const char *function_name, const std::string &message) } } -__attribute__((always_inline)) inline void -Logger::stderr_debug_log_alloca(const char *function_name, - const char *variable_name, size_t length) const { +__attribute__((always_inline)) inline void Logger::stderr_debug_log_alloca( + const char *function_name, const char *variable_name, size_t length) const { if (unlikely(verbose_flag)) { std::cerr << "asherah-node: [DEBUG] " << function_name << ": Calling alloca(" << length << ") (stack) for " @@ -198,7 +199,8 @@ Logger::stderr_error_log(const char *function_name, const char *message) const { } __attribute__((always_inline)) inline void -Logger::stderr_error_log(const char *function_name, const std::string &message) const { +Logger::stderr_error_log(const char *function_name, + const std::string &message) const { if (unlikely(verbose_flag)) { std::cerr << "asherah-node: [ERROR] " << function_name << ": " << message << std::endl @@ -207,7 +209,8 @@ Logger::stderr_error_log(const char *function_name, const std::string &message) } __attribute__((always_inline, noreturn)) inline void -Logger::log_error_and_throw(const char *function_name, const std::string &error_msg) const { +Logger::log_error_and_throw(const char *function_name, + const std::string &error_msg) const { error_log(function_name, error_msg); stderr_error_log(function_name, error_msg); throw std::runtime_error(function_name + (": " + error_msg)); diff --git a/src/logging.h b/src/logging.h index e9bb65a..c88b160 100644 --- a/src/logging.h +++ b/src/logging.h @@ -29,27 +29,32 @@ class Logger { void debug_log_copy_buffer(const char *function_name, const char *variable_name, size_t length) const; void debug_log_configure_cbuffer(const char *function_name, - const char *variable_name, size_t length) const; + const char *variable_name, size_t length) + const; */ void error_log(const char *function_name, const char *message) const; void error_log(const char *function_name, const std::string &message) const; - void log_error_and_throw(const char *function_name, const std::string &error_msg) const; + void log_error_and_throw(const char *function_name, + const std::string &error_msg) const; private: void stderr_debug_log(const char *function_name, const char *message) const; - void stderr_debug_log(const char *function_name, const std::string &message) const; + void stderr_debug_log(const char *function_name, + const std::string &message) const; void stderr_debug_log_alloca(const char *function_name, const char *variable_name, size_t length) const; void stderr_debug_log_new(const char *function_name, const char *variable_name, size_t length) const; void stderr_error_log(const char *function_name, const char *message) const; - void stderr_error_log(const char *function_name, const std::string &message) const; + void stderr_error_log(const char *function_name, + const std::string &message) const; - __attribute__((always_inline)) inline static std::string format_ptr(const char *ptr) { - std::ostringstream ss; - ss << "0x" << std::hex << (intptr_t)ptr; - return ss.str(); + __attribute__((always_inline)) inline static std::string + format_ptr(const char *ptr) { + std::ostringstream ss; + ss << "0x" << std::hex << (intptr_t)ptr; + return ss.str(); } int32_t verbose_flag = 0; diff --git a/src/napi_utils.h b/src/napi_utils.h index 34a3bf9..e243a26 100644 --- a/src/napi_utils.h +++ b/src/napi_utils.h @@ -6,89 +6,129 @@ class NapiUtils { public: - static void AsJsonObjectAndString(const Napi::Env &env, const Napi::Value& value, Napi::String& jsonString, Napi::Object& jsonObject) { - std::cerr << "AsJsonObjectAndString called" << std::endl << std::flush; - auto json = env.Global().Get("JSON").As(); - auto jsonStringify = json.Get("stringify").As(); - auto jsonParse = json.Get("parse").As(); - if(value.IsUndefined()) { - std::cerr << "AsJsonObjectAndString - Input value is undefined" << std::endl << std::flush; - throw std::invalid_argument("Input value is undefined"); - } else if (value.IsString()) { - // Convert string to object using JSON.parse - std::cerr << "AsJsonObjectAndString Convert string to object using JSON.parse" << std::endl << std::flush; - Napi::String str = value.As(); - jsonString = str; - jsonObject = jsonParse.Call({ str }).As(); - } else if (value.IsObject()) { - // Convert object to string using JSON.stringify - std::cerr << "AsJsonObjectAndString Cast Napi::Value to Napi::Object" << std::endl << std::flush; - Napi::Object obj = value.As(); + static void AsJsonObjectAndString(const Napi::Env &env, + const Napi::Value &value, + Napi::String &jsonString, + Napi::Object &jsonObject) { + std::cerr << "AsJsonObjectAndString called" << std::endl << std::flush; + auto json = env.Global().Get("JSON").As(); + auto jsonStringify = json.Get("stringify").As(); + auto jsonParse = json.Get("parse").As(); + if (value.IsUndefined()) { + std::cerr << "AsJsonObjectAndString - Input value is undefined" + << std::endl + << std::flush; + throw std::invalid_argument("Input value is undefined"); + } else if (value.IsString()) { + // Convert string to object using JSON.parse + std::cerr + << "AsJsonObjectAndString Convert string to object using JSON.parse" + << std::endl + << std::flush; + Napi::String str = value.As(); + jsonString = str; + jsonObject = jsonParse.Call({str}).As(); + } else if (value.IsObject()) { + // Convert object to string using JSON.stringify + std::cerr << "AsJsonObjectAndString Cast Napi::Value to Napi::Object" + << std::endl + << std::flush; + Napi::Object obj = value.As(); - std::cerr << "AsJsonObjectAndString Determine if jsonStringify is okay" << std::endl << std::flush; - if(jsonStringify.IsUndefined()) { - std::cerr << "AsJsonObjectAndString - jsonStringify is undefined" << std::endl << std::flush; - throw std::invalid_argument("jsonStringify is undefined"); - } else if(jsonStringify.IsEmpty()) { - std::cerr << "AsJsonObjectAndString - jsonStringify is empty" << std::endl << std::flush; - throw std::invalid_argument("jsonStringify is empty"); - } else { - std::cerr << "AsJsonObjectAndString - jsonStringify is okay" << std::endl << std::flush; - } + std::cerr << "AsJsonObjectAndString Determine if jsonStringify is okay" + << std::endl + << std::flush; + if (jsonStringify.IsUndefined()) { + std::cerr << "AsJsonObjectAndString - jsonStringify is undefined" + << std::endl + << std::flush; + throw std::invalid_argument("jsonStringify is undefined"); + } else if (jsonStringify.IsEmpty()) { + std::cerr << "AsJsonObjectAndString - jsonStringify is empty" + << std::endl + << std::flush; + throw std::invalid_argument("jsonStringify is empty"); + } else { + std::cerr << "AsJsonObjectAndString - jsonStringify is okay" + << std::endl + << std::flush; + } - std::cerr << "AsJsonObjectAndString Convert object to string using JSON.stringify" << std::endl << std::flush; - jsonString = jsonStringify.Call({ obj }).As(); - jsonObject = obj; - } else { - std::cerr << "AsJsonObjectAndString - Input value must be a Napi::Object or Napi::String" << std::endl << std::flush; - throw std::invalid_argument("Input value must be a Napi::Object or Napi::String"); - } - std::cerr << "AsJsonObjectAndString done" << std::endl << std::flush; + std::cerr << "AsJsonObjectAndString Convert object to string using " + "JSON.stringify" + << std::endl + << std::flush; + jsonString = jsonStringify.Call({obj}).As(); + jsonObject = obj; + } else { + std::cerr << "AsJsonObjectAndString - Input value must be a Napi::Object " + "or Napi::String" + << std::endl + << std::flush; + throw std::invalid_argument( + "Input value must be a Napi::Object or Napi::String"); } + std::cerr << "AsJsonObjectAndString done" << std::endl << std::flush; + } - static void GetStringProperty(const Napi::Object& obj, const char *propertyName, Napi::String& result) { - std::cerr << "GetStringProperty called" << std::endl << std::flush; - auto maybeValue = obj.Get(propertyName); + static void GetStringProperty(const Napi::Object &obj, + const char *propertyName, + Napi::String &result) { + std::cerr << "GetStringProperty called" << std::endl << std::flush; + auto maybeValue = obj.Get(propertyName); - if (!maybeValue.IsEmpty() && maybeValue.IsString()) { - result = maybeValue.As(); - } else { - std::cerr << "GetStringProperty - Property '" << propertyName << "' is not a Napi::String or is missing." << std::endl << std::flush; - throw std::invalid_argument("Property '" + std::string(propertyName) + "' is not a Napi::String or is missing."); - } - std::cerr << "GetStringProperty finished" << std::endl << std::flush; + if (!maybeValue.IsEmpty() && maybeValue.IsString()) { + result = maybeValue.As(); + } else { + std::cerr << "GetStringProperty - Property '" << propertyName + << "' is not a Napi::String or is missing." << std::endl + << std::flush; + throw std::invalid_argument("Property '" + std::string(propertyName) + + "' is not a Napi::String or is missing."); } + std::cerr << "GetStringProperty finished" << std::endl << std::flush; + } - static void GetBooleanProperty(const Napi::Object& obj, const char *propertyName, bool& result, bool defaultValue = false) { - std::cerr << "GetBooleanProperty called" << std::endl << std::flush; - auto maybeValue = obj.Get(propertyName); + static void GetBooleanProperty(const Napi::Object &obj, + const char *propertyName, bool &result, + bool defaultValue = false) { + std::cerr << "GetBooleanProperty called" << std::endl << std::flush; + auto maybeValue = obj.Get(propertyName); - if (!maybeValue.IsEmpty()) { - Napi::Value value = maybeValue; + if (!maybeValue.IsEmpty()) { + Napi::Value value = maybeValue; - if (value.IsBoolean()) { - result = value.As(); - } else { - // Coerce to boolean - result = value.ToBoolean(); - } - } else { - result = defaultValue; - } - std::cerr << "GetBooleanProperty finished" << std::endl << std::flush; + if (value.IsBoolean()) { + result = value.As(); + } else { + // Coerce to boolean + result = value.ToBoolean(); + } + } else { + result = defaultValue; } + std::cerr << "GetBooleanProperty finished" << std::endl << std::flush; + } - static size_t GetUtf8StringLength(const Napi::Env &env, const Napi::String& napiString) { - size_t result; - std::cerr << "GetUtf8StringLength: Calling napi_get_value_string_utf8" << std::endl << std::flush; - napi_status status = napi_get_value_string_utf8(env, napiString, nullptr, 0, &result); - if (status != napi_ok) { - std::cerr << "GetUtf8StringLength: Failed to get UTF-8 string length. Status: " << status << std::endl << std::flush; - throw std::runtime_error("Failed to get UTF-8 string length. Status: " + std::to_string(status)); - } - std::cerr << "GetUtf8StringLength: " << result << std::endl << std::flush; - return result; + static size_t GetUtf8StringLength(const Napi::Env &env, + const Napi::String &napiString) { + size_t result; + std::cerr << "GetUtf8StringLength: Calling napi_get_value_string_utf8" + << std::endl + << std::flush; + napi_status status = + napi_get_value_string_utf8(env, napiString, nullptr, 0, &result); + if (status != napi_ok) { + std::cerr + << "GetUtf8StringLength: Failed to get UTF-8 string length. Status: " + << status << std::endl + << std::flush; + throw std::runtime_error("Failed to get UTF-8 string length. Status: " + + std::to_string(status)); } + std::cerr << "GetUtf8StringLength: " << result << std::endl << std::flush; + return result; + } }; -#endif //NAPI_UTILS_H +#endif // NAPI_UTILS_H diff --git a/src/scoped_allocate.h b/src/scoped_allocate.h index 6bef0ec..33e5867 100644 --- a/src/scoped_allocate.h +++ b/src/scoped_allocate.h @@ -14,7 +14,7 @@ buffer = (char *)alloca(buffer_size); \ } else { \ /* Otherwise, allocate it on the heap */ \ - logger.debug_log_new(__func__, #buffer, buffer_size); \ + logger.debug_log_new(__func__, #buffer, buffer_size); \ buffer = new (std::nothrow) char[buffer_size]; \ if (unlikely(buffer == nullptr)) { \ std::string error_msg = \