diff --git a/apps/identity.json b/apps/identity.json index eaa9d030..5558d38e 100644 --- a/apps/identity.json +++ b/apps/identity.json @@ -1,5 +1,5 @@ { "type": "tildefriends-app", "emoji": "🪪", - "previous": "&XHcSLE9zb9QH0hFGAza/5wuIv8jj2NKARlOvMBpMAzc=.sha256" + "previous": "&1RskhkBW1UheJRrw7xcTjS8ELDMnBXNAuKqW5BiT72c=.sha256" } \ No newline at end of file diff --git a/apps/identity/app.js b/apps/identity/app.js index b1b0c3fc..b5d56bb5 100644 --- a/apps/identity/app.js +++ b/apps/identity/app.js @@ -3,33 +3,35 @@ import * as tfrpc from '/tfrpc.js'; tfrpc.register(async function get_private_key(id) { return bip39Words(await ssb.getPrivateKey(id)); }); +tfrpc.register(async function add_id(id) { + return await ssb.addIdentity(bip39Bytes(id)); +}); async function main() { - /* - let words = 'body hair useful camp warm into cause riot two bamboo kick educate dinosaur advice seed type crisp where guilt avocado output rely lunch goddess'; - let bytes = base64Decode('GO0Lv5BvcuuJJdHrokHoo0PmCDC/XjO/SZ6H+ddq4UvWd/VPW1RJrjd1aCUIfPIojFXrWMb8R54vVerU2TwjdQ==').slice(0, 32); - let data = { - ids: ids, - words: bip39Words(bytes), - bytes: base64Encode(bip39Bytes(words)), - round: bip39Words((await bip39Bytes(words)).slice(0, 32)), - privates: (await Promise.all(ids.map(id => ssb.getPrivateKey(id)))).map(x => bip39Words(x)), - };*/ let ids = await ssb.getIdentities(); await app.setDocument(` `+ + + `+ ids.map(id => `
`).join('\n')+ ``); } diff --git a/core/core.js b/core/core.js index 45e3d3ad..c70aff82 100644 --- a/core/core.js +++ b/core/core.js @@ -395,6 +395,15 @@ async function getProcessBlob(blobId, key, options) { return ssb.createIdentity(process.credentials.session.name); } }; + imports.ssb.addIdentity = function(id) { + if (process.credentials && + process.credentials.session && + process.credentials.session.name) { + return Promise.resolve(imports.core.permissionTest('ssb_id_add')).then(function() { + return ssb.addIdentity(process.credentials.session.name, id); + }); + } + }; imports.ssb.getOwnerIdentities = function() { if (options.packageOwner) { return ssb.getIdentities(options.packageOwner); diff --git a/src/bip39.c b/src/bip39.c index 2f44a01e..88f33968 100644 --- a/src/bip39.c +++ b/src/bip39.c @@ -113,10 +113,10 @@ bool tf_bip39_words_to_bytes(const char* words, uint8_t* out_bytes, size_t bytes } uint8_t data[33]; - crypto_hash_sha256(data, bytes, bytes_size); + crypto_hash_sha256(data, bytes, 32); if (data[0] != bytes[32]) { - tf_printf("%s: Checksum mismatch.\n", __func__); + tf_printf("%s: Checksum mismatch (%d vs. %d).\n", __func__, data[0], bytes[32]); return false; } diff --git a/src/ssb.js.c b/src/ssb.js.c index f4977413..63956217 100644 --- a/src/ssb.js.c +++ b/src/ssb.js.c @@ -10,6 +10,7 @@ #include "sodium/crypto_box.h" #include "sodium/crypto_scalarmult.h" #include "sodium/crypto_scalarmult_curve25519.h" +#include "sodium/crypto_scalarmult_ed25519.h" #include "sodium/crypto_secretbox.h" #include "sodium/crypto_sign.h" #include "sodium/randombytes.h" @@ -64,6 +65,80 @@ static JSValue _tf_ssb_createIdentity(JSContext* context, JSValueConst this_val, return result; } +static JSValue _tf_ssb_addIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId); + JSValue result = JS_UNDEFINED; + if (ssb) + { + const char* user = JS_ToCString(context, argv[0]); + + JSValue buffer = JS_UNDEFINED; + size_t length = 0; + uint8_t* array = tf_util_try_get_array_buffer(context, &length, argv[1]); + if (!array) + { + size_t offset; + size_t element_size; + buffer = tf_util_try_get_typed_array_buffer(context, argv[1], &offset, &length, &element_size); + if (!JS_IsException(buffer)) + { + array = tf_util_try_get_array_buffer(context, &length, buffer); + } + } + + if (array) + { + if (length == crypto_sign_SECRETKEYBYTES / 2) + { + uint8_t public_key[crypto_sign_PUBLICKEYBYTES]; + unsigned char seed[crypto_sign_SEEDBYTES]; + uint8_t secret_key[crypto_sign_SECRETKEYBYTES] = { 0 }; + memcpy(secret_key, array, sizeof(secret_key) / 2); + if (crypto_sign_ed25519_sk_to_seed(seed, secret_key) == 0 && + crypto_sign_seed_keypair(public_key, secret_key, seed) == 0) + { + char public_key_b64[512]; + tf_base64_encode(public_key, sizeof(public_key), public_key_b64, sizeof(public_key_b64)); + snprintf(public_key_b64 + strlen(public_key_b64), sizeof(public_key_b64) - strlen(public_key_b64), ".ed25519"); + + uint8_t combined[crypto_sign_SECRETKEYBYTES]; + memcpy(combined, array, length); + memcpy(combined + length, public_key, sizeof(public_key)); + char combined_b64[512]; + tf_base64_encode(combined, sizeof(combined), combined_b64, sizeof(combined_b64)); + snprintf(combined_b64 + strlen(combined_b64), sizeof(combined_b64) - strlen(combined_b64), ".ed25519"); + + if (tf_ssb_db_identity_add(ssb, user, public_key_b64, combined_b64)) + { + result = JS_TRUE; + } + else + { + tf_printf("Unable to add the identity."); + } + } + } + else + { + tf_printf("Unexpected private key size: %zd vs. %d\n", length, crypto_sign_SECRETKEYBYTES); + } + } + else + { + tf_printf("didn't find array\n"); + } + JS_FreeValue(context, buffer); + + JS_FreeCString(context, user); + } + else + { + tf_printf("no ssb\n"); + } + return result; +} + static JSValue _set_server_following_internal(tf_ssb_t* ssb, JSValueConst this_val, JSValue id, JSValue following) { JSContext* context = tf_ssb_get_context(ssb); @@ -1644,6 +1719,7 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb) /* Requires an identity. */ JS_SetPropertyStr(context, object, "createIdentity", JS_NewCFunction(context, _tf_ssb_createIdentity, "createIdentity", 1)); + JS_SetPropertyStr(context, object, "addIdentity", JS_NewCFunction(context, _tf_ssb_addIdentity, "addIdentity", 2)); JS_SetPropertyStr(context, object, "setServerFollowingMe", JS_NewCFunction(context, _tf_ssb_set_server_following_me, "setServerFollowingMe", 3)); JS_SetPropertyStr(context, object, "getIdentities", JS_NewCFunction(context, _tf_ssb_getIdentities, "getIdentities", 1)); JS_SetPropertyStr(context, object, "getPrivateKey", JS_NewCFunction(context, _tf_ssb_getPrivateKey, "getPrivateKey", 2)); diff --git a/src/util.js.c b/src/util.js.c index 607d35d9..c00348c6 100644 --- a/src/util.js.c +++ b/src/util.js.c @@ -159,7 +159,7 @@ static JSValue _util_bip39_words(JSContext* context, JSValueConst this_val, int static JSValue _util_bip39_bytes(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) { const char* words = JS_ToCString(context, argv[0]); - uint8_t bytes[33] = { 0 }; + uint8_t bytes[32] = { 0 }; bool success = tf_bip39_words_to_bytes(words, bytes, sizeof(bytes)); JS_FreeCString(context, words); if (success)