forked from cory/tildefriends
Tiny steps toward getting away from one global identity.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3932 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
ae5560f33a
commit
f764007fc6
14
core/core.js
14
core/core.js
@ -192,6 +192,20 @@ async function getProcessBlob(blobId, key, options) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
imports.ssb = Object.fromEntries(Object.keys(ssb).map(key => [key, ssb[key].bind(ssb)]));
|
imports.ssb = Object.fromEntries(Object.keys(ssb).map(key => [key, ssb[key].bind(ssb)]));
|
||||||
|
imports.ssb.createIdentity = function() {
|
||||||
|
if (process.credentials &&
|
||||||
|
process.credentials.session &&
|
||||||
|
process.credentials.session.name) {
|
||||||
|
return ssb.createIdentity(process.credentials.session.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
imports.ssb.getIdentities = function() {
|
||||||
|
if (process.credentials &&
|
||||||
|
process.credentials.session &&
|
||||||
|
process.credentials.session.name) {
|
||||||
|
return ssb.getIdentities(process.credentials.session.name);
|
||||||
|
}
|
||||||
|
};
|
||||||
if (process.credentials &&
|
if (process.credentials &&
|
||||||
process.credentials.session &&
|
process.credentials.session &&
|
||||||
process.credentials.session.name) {
|
process.credentials.session.name) {
|
||||||
|
28
src/ssb.c
28
src/ssb.c
@ -1267,11 +1267,8 @@ static bool _tf_ssb_connection_box_stream_recv(tf_ssb_connection_t* connection)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tf_ssb_append_message(tf_ssb_t* ssb, JSValue message)
|
void tf_ssb_append_message_with_keys(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message)
|
||||||
{
|
{
|
||||||
char author[k_id_base64_len];
|
|
||||||
tf_ssb_id_bin_to_str(author, sizeof(author), ssb->pub);
|
|
||||||
|
|
||||||
char previous_id[crypto_hash_sha256_BYTES * 2];
|
char previous_id[crypto_hash_sha256_BYTES * 2];
|
||||||
int64_t previous_sequence = 0;
|
int64_t previous_sequence = 0;
|
||||||
bool have_previous = tf_ssb_db_get_latest_message_by_author(ssb, author, &previous_sequence, previous_id, sizeof(previous_id));
|
bool have_previous = tf_ssb_db_get_latest_message_by_author(ssb, author, &previous_sequence, previous_id, sizeof(previous_id));
|
||||||
@ -1304,7 +1301,7 @@ void tf_ssb_append_message(tf_ssb_t* ssb, JSValue message)
|
|||||||
|
|
||||||
uint8_t signature[crypto_sign_BYTES];
|
uint8_t signature[crypto_sign_BYTES];
|
||||||
unsigned long long siglen;
|
unsigned long long siglen;
|
||||||
bool valid = crypto_sign_detached(signature, &siglen, (const uint8_t*)json, len, ssb->priv) == 0;
|
bool valid = crypto_sign_detached(signature, &siglen, (const uint8_t*)json, len, private_key) == 0;
|
||||||
|
|
||||||
JS_FreeCString(context, json);
|
JS_FreeCString(context, json);
|
||||||
JS_FreeValue(context, jsonval);
|
JS_FreeValue(context, jsonval);
|
||||||
@ -1342,6 +1339,13 @@ void tf_ssb_append_message(tf_ssb_t* ssb, JSValue message)
|
|||||||
JS_FreeValue(context, root);
|
JS_FreeValue(context, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tf_ssb_append_message(tf_ssb_t* ssb, JSValue message)
|
||||||
|
{
|
||||||
|
char author[k_id_base64_len];
|
||||||
|
tf_ssb_id_bin_to_str(author, sizeof(author), ssb->pub);
|
||||||
|
tf_ssb_append_message_with_keys(ssb, author, ssb->priv, message);
|
||||||
|
}
|
||||||
|
|
||||||
void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason)
|
void _tf_ssb_connection_destroy(tf_ssb_connection_t* connection, const char* reason)
|
||||||
{
|
{
|
||||||
tf_ssb_t* ssb = connection->ssb;
|
tf_ssb_t* ssb = connection->ssb;
|
||||||
@ -1825,6 +1829,20 @@ void tf_ssb_generate_keys(tf_ssb_t* ssb)
|
|||||||
crypto_sign_ed25519_keypair(ssb->pub, ssb->priv);
|
crypto_sign_ed25519_keypair(ssb->pub, ssb->priv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tf_ssb_generate_keys_buffer(char* out_public, size_t public_size, char* out_private, size_t private_size)
|
||||||
|
{
|
||||||
|
uint8_t public[crypto_sign_PUBLICKEYBYTES];
|
||||||
|
uint8_t private[crypto_sign_SECRETKEYBYTES];
|
||||||
|
crypto_sign_ed25519_keypair(public, private);
|
||||||
|
|
||||||
|
uint8_t buffer[512];
|
||||||
|
base64c_encode(public, sizeof(public), buffer, sizeof(buffer));
|
||||||
|
snprintf(out_public, public_size, "%s.ed25519", buffer);
|
||||||
|
|
||||||
|
base64c_encode(private, sizeof(private), buffer, sizeof(buffer));
|
||||||
|
snprintf(out_private, private_size, "%s.ed25519", buffer);
|
||||||
|
}
|
||||||
|
|
||||||
void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace)
|
void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace)
|
||||||
{
|
{
|
||||||
ssb->trace = trace;
|
ssb->trace = trace;
|
||||||
|
91
src/ssb.db.c
91
src/ssb.db.c
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include <base64c.h>
|
#include <base64c.h>
|
||||||
#include <sodium/crypto_hash_sha256.h>
|
#include <sodium/crypto_hash_sha256.h>
|
||||||
|
#include <sodium/crypto_sign.h>
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -69,6 +70,13 @@ void tf_ssb_db_init(tf_ssb_t* ssb)
|
|||||||
" last_success INTEGER,"
|
" last_success INTEGER,"
|
||||||
" UNIQUE(host, port, key)"
|
" UNIQUE(host, port, key)"
|
||||||
")");
|
")");
|
||||||
|
_tf_ssb_db_exec(db,
|
||||||
|
"CREATE TABLE IF NOT EXISTS identities ("
|
||||||
|
" user TEXT,"
|
||||||
|
" public_key TEXT UNIQUE,"
|
||||||
|
" private_key TEXT UNIQUE"
|
||||||
|
")");
|
||||||
|
_tf_ssb_db_exec(db, "CREATE INDEX IF NOT EXISTS identities_user ON identities (user, public_key)");
|
||||||
|
|
||||||
bool need_add_sequence_before_author = true;
|
bool need_add_sequence_before_author = true;
|
||||||
bool need_convert_timestamp_to_real = false;
|
bool need_convert_timestamp_to_real = false;
|
||||||
@ -690,3 +698,86 @@ bool tf_ssb_db_check(sqlite3* db, const char* check_author)
|
|||||||
JS_FreeRuntime(runtime);
|
JS_FreeRuntime(runtime);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int tf_ssb_db_identity_get_count_for_user(tf_ssb_t* ssb, const char* user)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
sqlite3* db = tf_ssb_get_db(ssb);
|
||||||
|
sqlite3_stmt* statement = NULL;
|
||||||
|
if (sqlite3_prepare(db, "SELECT COUNT(*) FROM identities WHERE user = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
count = sqlite3_column_int(statement, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_key, const char* private_key)
|
||||||
|
{
|
||||||
|
bool added = false;
|
||||||
|
sqlite3* db = tf_ssb_get_db(ssb);
|
||||||
|
sqlite3_stmt* statement = NULL;
|
||||||
|
if (sqlite3_prepare(db, "INSERT INTO identities (user, public_key, private_key) VALUES (?, ?, ?) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
||||||
|
sqlite3_bind_text(statement, 2, public_key, -1, NULL) == SQLITE_OK &&
|
||||||
|
sqlite3_bind_text(statement, 3, private_key, -1, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
added =
|
||||||
|
sqlite3_step(statement) == SQLITE_DONE &&
|
||||||
|
sqlite3_changes(db) != 0;
|
||||||
|
if (!added)
|
||||||
|
{
|
||||||
|
printf("Unable to add identity: %s.\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
return added;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(const char* identity, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
sqlite3* db = tf_ssb_get_db(ssb);
|
||||||
|
sqlite3_stmt* statement = NULL;
|
||||||
|
if (sqlite3_prepare(db, "SELECT public_key FROM identities WHERE user = ? ORDER BY public_key", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
callback((const char*)sqlite3_column_text(statement, 0), user_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const char* public_key, uint8_t* out_private_key, size_t private_key_size)
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
memset(out_private_key, 0, crypto_sign_SECRETKEYBYTES);
|
||||||
|
sqlite3* db = tf_ssb_get_db(ssb);
|
||||||
|
sqlite3_stmt* statement = NULL;
|
||||||
|
if (sqlite3_prepare(db, "SELECT private_key FROM identities WHERE user = ? AND public_key = ?", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
||||||
|
sqlite3_bind_text(statement, 2, (public_key && *public_key == '@') ? public_key + 1 : public_key, -1, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
const uint8_t* key = sqlite3_column_text(statement, 0);
|
||||||
|
int r = base64c_decode(key, sqlite3_column_bytes(statement, 0) - strlen(".ed25519"), out_private_key, private_key_size);
|
||||||
|
success = r > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
@ -17,3 +17,8 @@ JSValue tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue bi
|
|||||||
|
|
||||||
typedef struct sqlite3 sqlite3;
|
typedef struct sqlite3 sqlite3;
|
||||||
bool tf_ssb_db_check(sqlite3* db, const char* author);
|
bool tf_ssb_db_check(sqlite3* db, const char* author);
|
||||||
|
|
||||||
|
int tf_ssb_db_identity_get_count_for_user(tf_ssb_t* ssb, const char* user);
|
||||||
|
bool tf_ssb_db_identity_add(tf_ssb_t* ssb, const char* user, const char* public_key, const char* private_key);
|
||||||
|
void tf_ssb_db_identity_visit(tf_ssb_t* ssb, const char* user, void (*callback)(const char* identity, void* user_data), void* user_data);
|
||||||
|
bool tf_ssb_db_identity_get_private_key(tf_ssb_t* ssb, const char* user, const char* public_key, uint8_t* out_private_key, size_t private_key_size);
|
||||||
|
@ -64,6 +64,7 @@ sqlite3* tf_ssb_get_db(tf_ssb_t* ssb);
|
|||||||
uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb);
|
uv_loop_t* tf_ssb_get_loop(tf_ssb_t* ssb);
|
||||||
|
|
||||||
void tf_ssb_generate_keys(tf_ssb_t* ssb);
|
void tf_ssb_generate_keys(tf_ssb_t* ssb);
|
||||||
|
void tf_ssb_generate_keys_buffer(char* out_public, size_t public_size, char* out_private, size_t private_size);
|
||||||
|
|
||||||
void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace);
|
void tf_ssb_set_trace(tf_ssb_t* ssb, tf_trace_t* trace);
|
||||||
tf_trace_t* tf_ssb_get_trace(tf_ssb_t* ssb);
|
tf_trace_t* tf_ssb_get_trace(tf_ssb_t* ssb);
|
||||||
@ -73,6 +74,7 @@ JSContext* tf_ssb_get_context(tf_ssb_t* ssb);
|
|||||||
void tf_ssb_broadcast_listener_start(tf_ssb_t* ssb, bool linger);
|
void tf_ssb_broadcast_listener_start(tf_ssb_t* ssb, bool linger);
|
||||||
void tf_ssb_run(tf_ssb_t* ssb);
|
void tf_ssb_run(tf_ssb_t* ssb);
|
||||||
void tf_ssb_append_message(tf_ssb_t* ssb, JSValue message);
|
void tf_ssb_append_message(tf_ssb_t* ssb, JSValue message);
|
||||||
|
void tf_ssb_append_message_with_keys(tf_ssb_t* ssb, const char* author, const uint8_t* private_key, JSValue message);
|
||||||
void tf_ssb_append_post(tf_ssb_t* ssb, const char* text);
|
void tf_ssb_append_post(tf_ssb_t* ssb, const char* text);
|
||||||
bool tf_ssb_whoami(tf_ssb_t* ssb, char* out_id, size_t out_id_size);
|
bool tf_ssb_whoami(tf_ssb_t* ssb, char* out_id, size_t out_id_size);
|
||||||
|
|
||||||
|
85
src/ssb.js.c
85
src/ssb.js.c
@ -32,6 +32,87 @@ static JSValue _tf_ssb_whoami(JSContext* context, JSValueConst this_val, int arg
|
|||||||
return JS_NULL;
|
return JS_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_createIdentity(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]);
|
||||||
|
int count = tf_ssb_db_identity_get_count_for_user(ssb, user);
|
||||||
|
if (count < 16)
|
||||||
|
{
|
||||||
|
char public[512];
|
||||||
|
char private[512];
|
||||||
|
tf_ssb_generate_keys_buffer(public, sizeof(public), private, sizeof(private));
|
||||||
|
if (!tf_ssb_db_identity_add(ssb, user, public, private))
|
||||||
|
{
|
||||||
|
result = JS_ThrowInternalError(context, "Unable to add identity.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = JS_ThrowInternalError(context, "Too many identities for user.");
|
||||||
|
}
|
||||||
|
JS_FreeCString(context, user);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct _identities_visit_t
|
||||||
|
{
|
||||||
|
JSContext* context;
|
||||||
|
JSValue array;
|
||||||
|
int count;
|
||||||
|
} identities_visit_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_getIdentities_visit(const char* identity, void* data)
|
||||||
|
{
|
||||||
|
identities_visit_t* state = data;
|
||||||
|
JS_SetPropertyUint32(state->context, state->array, state->count++, JS_NewString(state->context, identity));
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_getIdentities(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
|
{
|
||||||
|
JSValue result = JS_NewArray(context);
|
||||||
|
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||||
|
if (ssb)
|
||||||
|
{
|
||||||
|
const char* user = JS_ToCString(context, argv[0]);
|
||||||
|
identities_visit_t state =
|
||||||
|
{
|
||||||
|
.context = context,
|
||||||
|
.array = result,
|
||||||
|
};
|
||||||
|
tf_ssb_db_identity_visit(ssb, user, _tf_ssb_getIdentities_visit, &state);
|
||||||
|
JS_FreeCString(context, user);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSValue _tf_ssb_appendMessageWithIdentity(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
|
{
|
||||||
|
JSValue result = JS_UNDEFINED;
|
||||||
|
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||||
|
if (ssb)
|
||||||
|
{
|
||||||
|
const char* user = JS_ToCString(context, argv[0]);
|
||||||
|
const char* id = JS_ToCString(context, argv[1]);
|
||||||
|
uint8_t private_key[crypto_sign_SECRETKEYBYTES];
|
||||||
|
if (tf_ssb_db_identity_get_private_key(ssb, user, id, private_key, sizeof(private_key)))
|
||||||
|
{
|
||||||
|
tf_ssb_append_message_with_keys(ssb, id, private_key, argv[2]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = JS_ThrowInternalError(context, "Unable to get private key for user %s with identity %s.", user, id);
|
||||||
|
}
|
||||||
|
JS_FreeCString(context, id);
|
||||||
|
JS_FreeCString(context, user);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue _tf_ssb_getMessage(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _tf_ssb_getMessage(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
JSValue result = JS_NULL;
|
JSValue result = JS_NULL;
|
||||||
@ -765,6 +846,10 @@ void tf_ssb_register(JSContext* context, tf_ssb_t* ssb)
|
|||||||
JSValue object = JS_NewObjectClass(context, _tf_ssb_classId);
|
JSValue object = JS_NewObjectClass(context, _tf_ssb_classId);
|
||||||
JS_SetPropertyStr(context, global, "ssb", object);
|
JS_SetPropertyStr(context, global, "ssb", object);
|
||||||
JS_SetOpaque(object, ssb);
|
JS_SetOpaque(object, ssb);
|
||||||
|
JS_SetPropertyStr(context, object, "createIdentity", JS_NewCFunction(context, _tf_ssb_createIdentity, "createIdentity", 1));
|
||||||
|
JS_SetPropertyStr(context, object, "getIdentities", JS_NewCFunction(context, _tf_ssb_getIdentities, "getIdentities", 1));
|
||||||
|
JS_SetPropertyStr(context, object, "appendMessageWithIdentity", JS_NewCFunction(context, _tf_ssb_appendMessageWithIdentity, "appendMessageWithIdentity", 3));
|
||||||
|
|
||||||
JS_SetPropertyStr(context, object, "whoami", JS_NewCFunction(context, _tf_ssb_whoami, "whoami", 0));
|
JS_SetPropertyStr(context, object, "whoami", JS_NewCFunction(context, _tf_ssb_whoami, "whoami", 0));
|
||||||
JS_SetPropertyStr(context, object, "getMessage", JS_NewCFunction(context, _tf_ssb_getMessage, "getMessage", 2));
|
JS_SetPropertyStr(context, object, "getMessage", JS_NewCFunction(context, _tf_ssb_getMessage, "getMessage", 2));
|
||||||
JS_SetPropertyStr(context, object, "blobGet", JS_NewCFunction(context, _tf_ssb_blobGet, "blobGet", 1));
|
JS_SetPropertyStr(context, object, "blobGet", JS_NewCFunction(context, _tf_ssb_blobGet, "blobGet", 1));
|
||||||
|
Loading…
Reference in New Issue
Block a user