From b16c37e48bb2db5c12bae7f3f91a44c0e16b5e43 Mon Sep 17 00:00:00 2001 From: Cory McWilliams Date: Sun, 16 Jun 2024 17:22:26 -0400 Subject: [PATCH] Make ssb.privateMessageDecrypt do its work not on the main thread. I think that's finally everything for real. --- src/ssb.js.c | 93 ++++++++++++++++++++++++++++++++++++++----------- src/ssb.tests.c | 3 +- 2 files changed, 75 insertions(+), 21 deletions(-) diff --git a/src/ssb.js.c b/src/ssb.js.c index df40aa7c..d49cac45 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -2046,25 +2046,31 @@ static JSValue _tf_ssb_private_message_encrypt(JSContext* context, JSValueConst return result; } -static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +typedef struct _private_message_decrypt_t { - JSValue result = JS_UNDEFINED; - const char* user = JS_ToCString(context, argv[0]); - const char* identity = JS_ToCString(context, argv[1]); - size_t message_size = 0; - const char* message = JS_ToCStringLen(context, &message_size, argv[2]); + const char* user; + const char* identity; + size_t message_size; + const char* message; + const char* decrypted; + size_t decrypted_size; + const char* error; + JSValue promise[2]; +} private_message_decrypt_t; + +static void _tf_ssb_private_message_decrypt_work(tf_ssb_t* ssb, void* user_data) +{ + private_message_decrypt_t* work = user_data; uint8_t private_key[crypto_sign_SECRETKEYBYTES] = { 0 }; - - tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); sqlite3* db = tf_ssb_acquire_db_reader(ssb); - bool found = _tf_ssb_get_private_key_curve25519(db, user, identity, private_key); + bool found = _tf_ssb_get_private_key_curve25519(db, work->user, work->identity, private_key); tf_ssb_release_db_reader(ssb, db); if (found) { - uint8_t* decoded = tf_malloc(message_size); - int decoded_length = tf_base64_decode(message, message_size - strlen(".box"), decoded, message_size); + uint8_t* decoded = tf_malloc(work->message_size); + int decoded_length = tf_base64_decode(work->message, work->message_size - strlen(".box"), decoded, work->message_size); uint8_t* nonce = decoded; uint8_t* public_key = decoded + crypto_box_NONCEBYTES; if (public_key + crypto_secretbox_KEYBYTES < decoded + decoded_length) @@ -2090,36 +2096,83 @@ static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst uint8_t* key = out + 1; if (crypto_secretbox_open_easy(decrypted, body, body_size, nonce, key) != -1) { - result = JS_NewStringLen(context, (const char*)decrypted, body_size - crypto_secretbox_MACBYTES); + work->decrypted = (const char*)decrypted; + work->decrypted_size = body_size - crypto_secretbox_MACBYTES; } else { - result = JS_ThrowInternalError(context, "Received key to open secret box containing message body, but it did not work."); + work->error = "Received key to open secret box containing message body, but it did not work."; } - tf_free(decrypted); } } } else { - result = JS_ThrowInternalError(context, "crypto_scalarmult failed."); + work->error = "crypto_scalarmult failed."; } } else { - result = JS_ThrowInternalError(context, "Encrypted message was not long enough to contains its one-time public key."); + work->error = "Encrypted message was not long enough to contains its one-time public key."; } tf_free(decoded); } else { - result = JS_ThrowInternalError(context, "Private key not found for user %s with id %s.", user, identity); + work->error = "Private key not found for user."; } +} - JS_FreeCString(context, user); - JS_FreeCString(context, identity); - JS_FreeCString(context, message); +static void _tf_ssb_private_message_decrypt_after_work(tf_ssb_t* ssb, int status, void* user_data) +{ + private_message_decrypt_t* work = user_data; + JSContext* context = tf_ssb_get_context(ssb); + JSValue error = JS_UNDEFINED; + if (work->error) + { + JSValue result = JS_ThrowInternalError(context, "%s", work->error); + error = JS_Call(context, work->promise[1], JS_UNDEFINED, 1, &result); + JS_FreeValue(context, result); + } + else if (work->decrypted) + { + JSValue result = JS_NewStringLen(context, work->decrypted, work->decrypted_size); + error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result); + JS_FreeValue(context, result); + } + else + { + JSValue result = JS_UNDEFINED; + error = JS_Call(context, work->promise[0], JS_UNDEFINED, 1, &result); + } + tf_util_report_error(context, error); + JS_FreeValue(context, error); + JS_FreeValue(context, work->promise[0]); + JS_FreeValue(context, work->promise[1]); + JS_FreeCString(context, work->user); + JS_FreeCString(context, work->identity); + JS_FreeCString(context, work->message); + tf_free((void*)work->decrypted); + tf_free(work); +} +static JSValue _tf_ssb_private_message_decrypt(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); + const char* user = JS_ToCString(context, argv[0]); + const char* identity = JS_ToCString(context, argv[1]); + size_t message_size = 0; + const char* message = JS_ToCStringLen(context, &message_size, argv[2]); + + private_message_decrypt_t* work = tf_malloc(sizeof(private_message_decrypt_t)); + *work = (private_message_decrypt_t) { + .user = user, + .identity = identity, + .message_size = message_size, + .message = message, + }; + JSValue result = JS_NewPromiseCapability(context, work->promise); + tf_ssb_run_work(ssb, _tf_ssb_private_message_decrypt_work, _tf_ssb_private_message_decrypt_after_work, work); return result; } diff --git a/src/ssb.tests.c b/src/ssb.tests.c index ad620d27..186e5731 100644 --- a/src/ssb.tests.c +++ b/src/ssb.tests.c @@ -847,10 +847,11 @@ void tf_ssb_test_encrypt(const tf_test_options_t* options) "async function main() {\n" " let a = await ssb.createIdentity('test');\n" " let b = await ssb.createIdentity('test');\n" - " let c = await ssb.privateMessageEncrypt('test', a, [a, b], {'foo': 1});\n" + " let c = await ssb.privateMessageEncrypt('test', a, [a, b], \"{'foo': 1}\");\n" " if (!c.endsWith('.box')) {\n" " exit(1);\n" " }\n" + " print(await ssb.privateMessageDecrypt('test', a, c));\n" "}\n" "main().catch(() => exit(2));\n");