diff --git a/src/asherah.cc b/src/asherah.cc index 8fe5abd..032cdc9 100644 --- a/src/asherah.cc +++ b/src/asherah.cc @@ -155,13 +155,16 @@ class Asherah : public Napi::Addon { try { Napi::String partition_id_string; Napi::Value input_value; + size_t partition_id_length; - BeginEncryptToJson(env, __func__, info, partition_id_string, input_value); + BeginEncryptToJson(env, __func__, info, partition_id_string, input_value, + partition_id_length); #ifdef USE_SCOPED_ALLOCATE_BUFFER char *partition_id_cbuffer; size_t partition_id_cbuffer_size = - CobhanBufferNapi::StringToAllocationSize(env, partition_id_string); + CobhanBufferNapi::StringToAllocationSize(env, partition_id_string, + partition_id_length); SCOPED_ALLOCATE_BUFFER(partition_id_cbuffer, partition_id_cbuffer_size, maximum_stack_alloc_size, __func__); @@ -177,7 +180,8 @@ class Asherah : public Napi::Addon { CobhanBufferNapi input(env, input_value, input_cbuffer, input_cbuffer_size); #else - CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi partition_id(env, partition_id_string, + partition_id_length); CobhanBufferNapi input(env, input_value); #endif @@ -218,9 +222,12 @@ class Asherah : public Napi::Addon { try { Napi::String partition_id_string; Napi::Value input_value; - BeginEncryptToJson(env, __func__, info, partition_id_string, input_value); + size_t partition_id_length; + BeginEncryptToJson(env, __func__, info, partition_id_string, input_value, + partition_id_length); - CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi partition_id(env, partition_id_string, + partition_id_length); CobhanBufferNapi input(env, input_value); size_t partition_id_data_len_bytes = partition_id.get_data_len_bytes(); @@ -250,14 +257,16 @@ class Asherah : public Napi::Addon { try { Napi::String partition_id_string; Napi::Value input_value; + size_t partition_id_length; BeginDecryptFromJson(env, __func__, info, partition_id_string, - input_value); + input_value, partition_id_length); #ifdef USE_SCOPED_ALLOCATE_BUFFER char *partition_id_cbuffer; size_t partition_id_cbuffer_size = - CobhanBufferNapi::StringToAllocationSize(env, partition_id_string); + CobhanBufferNapi::StringToAllocationSize(env, partition_id_string, + partition_id_length); SCOPED_ALLOCATE_BUFFER(partition_id_cbuffer, partition_id_cbuffer_size, maximum_stack_alloc_size, __func__); @@ -280,7 +289,8 @@ class Asherah : public Napi::Addon { maximum_stack_alloc_size, __func__); CobhanBufferNapi output(env, output_cobhan_buffer, output_size_bytes); #else - CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi partition_id(env, partition_id_string, + partition_id_length); CobhanBufferNapi input(env, input_value); CobhanBufferNapi output(env, input.get_data_len_bytes()); #endif @@ -309,10 +319,12 @@ class Asherah : public Napi::Addon { try { Napi::String partition_id_string; Napi::Value input_value; + size_t partition_id_length; BeginDecryptFromJson(env, __func__, info, partition_id_string, - input_value); + input_value, partition_id_length); - CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi partition_id(env, partition_id_string, + partition_id_length); CobhanBufferNapi input(env, input_value); CobhanBufferNapi output(env, input.get_data_len_bytes()); @@ -338,13 +350,15 @@ class Asherah : public Napi::Addon { Napi::String partition_id_string; Napi::Value input_value; + size_t partition_id_length; BeginDecryptFromJson(env, __func__, info, partition_id_string, - input_value); + input_value, partition_id_length); #ifdef USE_SCOPED_ALLOCATE_BUFFER char *partition_id_cbuffer; size_t partition_id_cbuffer_size = - CobhanBufferNapi::StringToAllocationSize(env, partition_id_string); + CobhanBufferNapi::StringToAllocationSize(env, partition_id_string, + partition_id_length); SCOPED_ALLOCATE_BUFFER(partition_id_cbuffer, partition_id_cbuffer_size, maximum_stack_alloc_size, __func__); @@ -362,7 +376,8 @@ class Asherah : public Napi::Addon { CobhanBufferNapi output(env, input.get_data_len_bytes()); #else - CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi partition_id(env, partition_id_string, + partition_id_length); CobhanBufferNapi input(env, input_value); CobhanBufferNapi output(env, input.get_data_len_bytes()); #endif @@ -388,10 +403,12 @@ class Asherah : public Napi::Addon { Napi::String partition_id_string; Napi::Value input_value; + size_t partition_id_length; BeginDecryptFromJson(env, __func__, info, partition_id_string, - input_value); + input_value, partition_id_length); - CobhanBufferNapi partition_id(env, partition_id_string); + CobhanBufferNapi partition_id(env, partition_id_string, + partition_id_length); CobhanBufferNapi input(env, input_value); CobhanBufferNapi output(env, input.get_data_len_bytes()); @@ -549,13 +566,20 @@ class Asherah : public Napi::Addon { void BeginEncryptToJson(const Napi::Env &env, const char *func_name, const Napi::CallbackInfo &info, - Napi::String &partition_id, Napi::Value &input) { + Napi::String &partition_id, Napi::Value &input, + size_t &partition_id_length) { RequireAsherahSetup(env, func_name); NapiUtils::RequireParameterCount(info, 2); - partition_id = NapiUtils::RequireParameterString(env, func_name, info[0]); + partition_id = NapiUtils::RequireParameterStringWithLength( + env, func_name, info[0], partition_id_length); input = NapiUtils::RequireParameterStringOrBuffer(env, func_name, info[1]); + + if (partition_id_length == 0) { + NapiUtils::ThrowException(env, std::string(func_name) + + ": Partition ID cannot be empty"); + } } void EndEncryptToJson(Napi::Env env, CobhanBufferNapi &output, GoInt32 result, @@ -575,13 +599,20 @@ class Asherah : public Napi::Addon { void BeginDecryptFromJson(const Napi::Env &env, const char *func_name, const Napi::CallbackInfo &info, - Napi::String &partition_id, Napi::Value &input) { + Napi::String &partition_id, Napi::Value &input, + size_t &partition_id_length) { RequireAsherahSetup(env, func_name); NapiUtils::RequireParameterCount(info, 2); - partition_id = NapiUtils::RequireParameterString(env, func_name, info[0]); + partition_id = NapiUtils::RequireParameterStringWithLength( + env, func_name, info[0], partition_id_length); input = NapiUtils::RequireParameterStringOrBuffer(env, func_name, info[1]); + + if (partition_id_length == 0) { + NapiUtils::ThrowException(env, std::string(func_name) + + ": Partition ID cannot be empty"); + } } void EndDecryptFromJson(Napi::Env &env, CobhanBufferNapi &output, diff --git a/src/cobhan_buffer_napi.h b/src/cobhan_buffer_napi.h index 775c552..bf3e379 100644 --- a/src/cobhan_buffer_napi.h +++ b/src/cobhan_buffer_napi.h @@ -9,8 +9,12 @@ 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), + CobhanBufferNapi(const Napi::Env &env, const Napi::String &napiString, + int64_t utf8_length = -1) + : CobhanBuffer((utf8_length >= 0 + ? static_cast(utf8_length) + : NapiUtils::GetUtf8StringLength(env, napiString)) + + 1), env(env) { // Add one for possible NULL delimiter due to Node // string functions copy_from_string(napiString); @@ -117,8 +121,11 @@ class CobhanBufferNapi : public CobhanBuffer { // 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); + const Napi::String &napiString, + int64_t utf8_length = -1) { + size_t str_len = (utf8_length >= 0) + ? static_cast(utf8_length) + : NapiUtils::GetUtf8StringLength(env, napiString); return DataSizeToAllocationSize(str_len) + 1; // Add one for possible NULL delimiter due to Node string // functions diff --git a/src/napi_utils.h b/src/napi_utils.h index dcea5b8..2bd7892 100644 --- a/src/napi_utils.h +++ b/src/napi_utils.h @@ -119,6 +119,16 @@ class NapiUtils { } } + // Version that also returns the UTF-8 length to avoid redundant calls + static Napi::String RequireParameterStringWithLength(const Napi::Env &env, + const char *func_name, + Napi::Value value, + size_t &utf8_length) { + Napi::String str = RequireParameterString(env, func_name, value); + utf8_length = GetUtf8StringLength(env, str); + return str; + } + __attribute__((unused)) static Napi::Buffer RequireParameterBuffer(const Napi::Env &env, const char *func_name, Napi::Value value) { diff --git a/test/asherah.spec.ts b/test/asherah.spec.ts index 5e88145..3483036 100644 --- a/test/asherah.spec.ts +++ b/test/asherah.spec.ts @@ -66,6 +66,22 @@ describe('Asherah', function () { await bad_encrypt_buffers_empty_async(test_verbose, true, default_max_stack_alloc_item_size); }); + it('Bad Empty Partition ID Encrypt Sync', async function () { + await bad_empty_partition_id_encrypt_sync(test_verbose, true, default_max_stack_alloc_item_size); + }); + + it('Bad Empty Partition ID Encrypt Async', async function () { + await bad_empty_partition_id_encrypt_async(test_verbose, true, default_max_stack_alloc_item_size); + }); + + it('Bad Empty Partition ID Decrypt Sync', async function () { + await bad_empty_partition_id_decrypt_sync(test_verbose, true, default_max_stack_alloc_item_size); + }); + + it('Bad Empty Partition ID Decrypt Async', async function () { + await bad_empty_partition_id_decrypt_async(test_verbose, true, default_max_stack_alloc_item_size); + }); + // Test using heap only register_sync_roundtrip_tests(simple_secret, test_verbose, false, force_use_heap); register_sync_roundtrip_tests(simple_secret, test_verbose, true, force_use_heap); @@ -479,6 +495,50 @@ async function bad_encrypt_buffers_empty_async(verbose: boolean, session_cache: } } +async function bad_empty_partition_id_encrypt_sync(verbose: boolean, session_cache: boolean, max_stack_alloc_item_size: number): Promise { + await asherah_setup_static_memory_async(verbose, session_cache, max_stack_alloc_item_size); + try { + assert.throws(() => { + encrypt_string('', 'test data'); + }, Error); + } finally { + await asherah_shutdown_async(); + } +} + +async function bad_empty_partition_id_encrypt_async(verbose: boolean, session_cache: boolean, max_stack_alloc_item_size: number): Promise { + await asherah_setup_static_memory_async(verbose, session_cache, max_stack_alloc_item_size); + try { + await assert_throws_async(async () => { + await encrypt_string_async('', 'test data'); + }, 'Should throw error if partition ID is empty'); + } finally { + await asherah_shutdown_async(); + } +} + +async function bad_empty_partition_id_decrypt_sync(verbose: boolean, session_cache: boolean, max_stack_alloc_item_size: number): Promise { + await asherah_setup_static_memory_async(verbose, session_cache, max_stack_alloc_item_size); + try { + assert.throws(() => { + decrypt_string('', 'some-encrypted-data'); + }, Error); + } finally { + await asherah_shutdown_async(); + } +} + +async function bad_empty_partition_id_decrypt_async(verbose: boolean, session_cache: boolean, max_stack_alloc_item_size: number): Promise { + await asherah_setup_static_memory_async(verbose, session_cache, max_stack_alloc_item_size); + try { + await assert_throws_async(async () => { + await decrypt_string_async('', 'some-encrypted-data'); + }, 'Should throw error if partition ID is empty'); + } finally { + await asherah_shutdown_async(); + } +} + function get_test_name(prefix: string, verbose: boolean, session_cache: boolean, max_stack_alloc_item_size: number): string { return prefix + ' (' + (max_stack_alloc_item_size == 0 ? 'heap' : 'stack ' + max_stack_alloc_item_size) + ') Cache: ' + session_cache + ' Verbose: ' + verbose; }