diff --git a/Makefile b/Makefile index 2819161feb45d3dca63abb129a64bc8ec509912c..ffd284c8516f251dbe049a40a9c9b276bc942020 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -default: bsspeke +default: demo monocypher.o: monocypher.c monocypher.h cc -c monocypher.c @@ -6,7 +6,10 @@ monocypher.o: monocypher.c monocypher.h bsspeke.o: bsspeke.c bsspeke.h monocypher.h cc -c bsspeke.c -bsspeke: bsspeke.o monocypher.o - cc -o bsspeke bsspeke.o monocypher.o +demo.o: demo.c bsspeke.h + cc -c demo.c + +demo: demo.o bsspeke.o monocypher.o + cc -o demo demo.o bsspeke.o monocypher.o diff --git a/bsspeke.c b/bsspeke.c index 3d19f4c33e014c233bd0ff0cbea747582c95ce45..40aa2afd4edbf64bc349a3ac5a76c3a299418d8f 100644 --- a/bsspeke.c +++ b/bsspeke.c @@ -25,6 +25,34 @@ #include "monocypher.h" #include "bsspeke.h" +typedef enum { + DEBUG, + INFO, + WARN, + ERROR, + FATAL +} debug_level_t; + +debug_level_t curr_level = DEBUG; + +void debug(debug_level_t level, + const char *msg +) { + //if( level >= curr_level ) { + puts(msg); + //} +} + +void print_point(const char *label, + uint8_t point[32] +) { + printf("%8s:\t[", label); + int i = 31; + for(i=31; i >= 0; i--) + printf("%02x", point[i]); + printf("]\n"); +} + void bsspeke_client_init(bsspeke_client_ctx *ctx, const char* client_id, const size_t client_id_len, @@ -42,77 +70,226 @@ bsspeke_client_init(bsspeke_client_ctx *ctx, } void -bsspeke_client_generate_message1(bsspeke_msg1_t *msg1, - bsspeke_client_ctx *client +bsspeke_server_init(bsspeke_server_ctx *ctx, + const char* server_id, const size_t server_id_len +) { + ctx->server_id = (uint8_t *)server_id; + ctx->server_id_len = server_id_len; +} + +void +bsspeke_client_generate_message1 +( + bsspeke_login_msg1_t *msg1, + bsspeke_client_ctx *client ) { + debug(DEBUG, "Hashing client's password"); // 1. Hash the client's password, client_id, server_id to a point on the curve uint8_t scalar_hash[32]; uint8_t curve_point[32]; { - crypto_blake2b_ctx bctx; + crypto_blake2b_ctx hash_ctx; // Give us a 256 bit (32 byte) hash; Don't use a key - crypto_blake2b_general_init(&bctx, 32, NULL, 0); + crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0); // Add the client id, server id, and the password to the hash - crypto_blake2b_update(&bctx, + crypto_blake2b_update(&hash_ctx, (const uint8_t *)(client->password), client->password_len); - crypto_blake2b_update(&bctx, + crypto_blake2b_update(&hash_ctx, (const uint8_t *)(client->client_id), client->client_id_len); - crypto_blake2b_update(&bctx, + crypto_blake2b_update(&hash_ctx, (const uint8_t *)(client->server_id), client->server_id_len); // Write the digest value into `scalar_hash` - crypto_blake2b_final(&bctx, scalar_hash); + crypto_blake2b_final(&hash_ctx, scalar_hash); } + debug(DEBUG, "Mapping password hash onto the curve"); // Now use Elligator to map our scalar hash to a point on the curve crypto_hidden_to_curve(curve_point, scalar_hash); + print_point("H(pass)", curve_point); + // 2. Generate random r // * Actually generate 1/r first, and clamp() it // That way we know it will always lead us back to a point on the curve // * Then use the inverse of 1/r as `r` // FIXME: On second thought, monocypher seems to handle all of this complexity for us. Let's see what happens if we just do things the straightforward way for now... + debug(DEBUG, "Generating random blind `r`"); arc4random_buf(client->r, 32); + print_point("r", client->r); + debug(DEBUG, "Clamping r"); crypto_x25519_clamp(client->r); + print_point("r", client->r); + + debug(DEBUG, "Multiplying curve point by r"); // 3. Multiply our curve point by r crypto_x25519_scalarmult(msg1->blind, client->r, curve_point, 256); + print_point("blind", msg1->blind); + debug(DEBUG, "Done"); return; } void -bsspeke_server_init(bsspeke_server_ctx *ctx, - const char* server_id +bsspeke_client_setup_generate_message1 +( + bsspeke_setup_msg1_t *msg1, + bsspeke_client_ctx *client +) { + bsspeke_client_generate_message1(msg1, client); +} + +void +bsspeke_server_setup_generate_message2 + ( + bsspeke_setup_msg2_t *msg2, + const bsspeke_setup_msg1_t *msg1, + uint8_t *salt, const size_t salt_len, + bsspeke_server_ctx *server_ctx + ) +{ + // We're setting up a new account + // So we have to create a new random salt for the user + debug(DEBUG, "Generating new salt"); + arc4random_buf(salt, salt_len); + print_point("salt", salt); + + // Hash the salt + debug(DEBUG, "Hashing the salt"); + uint8_t H_salt[32]; + crypto_blake2b_general(H_salt, 32, NULL, 0, salt, salt_len); + // Use clamp() to ensure we stay on the curve in the multiply below + crypto_x25519_clamp(H_salt); + print_point("H_salt", H_salt); + // Multiply H(salt) by msg1->blind, save into msg2->blind_salt + debug(DEBUG, "Multiplying H_salt by the user's blind"); + crypto_x25519_scalarmult(msg2->blind_salt, H_salt, msg1->blind, 256); + print_point("blndsalt", msg2->blind_salt); +} + +int +bsspeke_client_setup_generate_message3 + ( + bsspeke_setup_msg3_t *msg3, + const bsspeke_setup_msg2_t *msg2, + uint32_t phf_blocks, uint32_t phf_iterations, + bsspeke_client_ctx *client + ) +{ + // Sanity checks first, before we do any work + if( phf_blocks < BSSPEKE_MIN_PHF_BLOCKS ) { + return -1; + } + if( phf_iterations < BSSPEKE_MIN_PHF_ITERATIONS ) { + return -1; + } + + uint8_t oblivious_salt[32]; + // Multiply the blinded salt by 1/r to get the oblivious salt + // Here we rely on Monocypher to do the heavy lifting for us + debug(DEBUG, "Removing the blind from the oblivious salt"); + crypto_x25519_inverse(oblivious_salt, client->r, msg2->blind_salt); + print_point("obv_salt", oblivious_salt); + + // Hash the oblivious salt together with the id's to create the salt for the PHF + uint8_t password_hash[64]; + + debug(DEBUG, "Creating the salt for the PHF"); + uint8_t phf_salt[32]; + { + crypto_blake2b_ctx hash_ctx; + crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0); + crypto_blake2b_update(&hash_ctx, oblivious_salt, 32); + crypto_blake2b_update(&hash_ctx, + client->client_id, + client->client_id_len); + crypto_blake2b_update(&hash_ctx, + client->server_id, + client->server_id_len); + crypto_blake2b_final(&hash_ctx, phf_salt); + } + print_point("phf_salt", phf_salt); + + debug(DEBUG, "Running the PHF to generate p and v"); + void *work_area; + if ((work_area = malloc(phf_blocks * 1024)) == NULL) { + return -1; + } + crypto_argon2i(password_hash, 64, work_area, + phf_blocks, phf_iterations, + client->password, client->password_len, + phf_salt, 32); + free(work_area); + + // p || v = pwKdf(password, BlindSalt, idC, idS, settings) + uint8_t *p = &(password_hash[0]); + uint8_t *v = &(password_hash[32]); + // FIXME Should we clamp() v before we multiply??? + crypto_x25519_clamp(v); + print_point("p", p); + print_point("v", v); + + // Hash p onto the curve to get this user's base point P + //uint8_t P[32]; + debug(DEBUG, "Hashing p onto the curve to get P"); + crypto_hidden_to_curve(msg3->P, p); + print_point("P", msg3->P); + + // Generate our long-term public key V = v * P + debug(DEBUG, "V = v * P"); + crypto_x25519_scalarmult(msg3->V, v, msg3->P, 256); + print_point("V", msg3->V); + + return 0; +} + + +void +bsspeke_client_login_generate_message1 +( + bsspeke_login_msg1_t *msg1, + bsspeke_client_ctx *client ) { - ctx->server_id = server_id; + bsspeke_client_generate_message1(msg1, client); } void -bsspeke_server_generate_message2(bsspeke_msg2_t *msg2, - const bsspeke_msg1_t *msg1, +bsspeke_server_login_generate_message2(bsspeke_login_msg2_t *msg2, + const bsspeke_login_msg1_t *msg1, const uint8_t *salt, const size_t salt_len, uint8_t P[32], uint8_t V[32], uint32_t phf_blocks, uint32_t phf_iterations, - bsspeke_server_ctx *server_ctx + bsspeke_server_ctx *server ) { // Hash the salt + debug(DEBUG, "Hashing the salt"); uint8_t H_salt[32]; crypto_blake2b_general(H_salt, 32, NULL, 0, salt, salt_len); // Use clamp() to ensure we stay on the curve in the multiply below crypto_x25519_clamp(H_salt); + print_point("H_salt", H_salt); // Multiply H(salt) by msg1->blind, save into msg2->blind_salt + debug(DEBUG, "Multiplying H_salt by the user's blind"); crypto_x25519_scalarmult(msg2->blind_salt, H_salt, msg1->blind, 256); + print_point("blndsalt", msg2->blind_salt); // Generate random ephemeral private key b, save it in ctx->b - arc4random_buf(server_ctx->b, 32); - crypto_x25519_clamp(server_ctx->b); + debug(DEBUG, "Generating ephemeral private key b"); + arc4random_buf(server->b, 32); + crypto_x25519_clamp(server->b); + print_point("b", server->b); + + debug(DEBUG, "Using client's base point P"); + print_point("P", P); // Compute public key B = b * P, save it in msg2->B - crypto_x25519_scalarmult(server_ctx->B, server_ctx->b, P, 256); + debug(DEBUG, "Computing ephemeral public key B = b * P"); + crypto_x25519_scalarmult(server->B, server->b, P, 256); + print_point("B", server->B); // Copy the public key into the outgoing message as well - memcpy(msg2->B, server_ctx->B, 32); + memcpy(msg2->B, server->B, 32); // Copy the PHF params too msg2->phf_blocks = phf_blocks; @@ -121,107 +298,134 @@ bsspeke_server_generate_message2(bsspeke_msg2_t *msg2, return; } + int -bsspeke_client_generate_message3(bsspeke_msg3_t *msg3, - const bsspeke_msg2_t *msg2, - bsspeke_client_ctx *client_ctx +bsspeke_client_login_generate_message3(bsspeke_login_msg3_t *msg3, + const bsspeke_login_msg2_t *msg2, + bsspeke_client_ctx *client ) { // Sanity checks first, before we do any work if( msg2->phf_blocks < BSSPEKE_MIN_PHF_BLOCKS ) { + debug(ERROR, "phf_blocks is too low. Aborting."); return -1; } if( msg2->phf_iterations < BSSPEKE_MIN_PHF_ITERATIONS ) { + debug(ERROR, "phf_iterations is too low. Aborting."); return -1; } uint8_t oblivious_salt[32]; // Multiply the blinded salt by 1/r to get the oblivious salt // Here we rely on Monocypher to do the heavy lifting for us - crypto_x25519_inverse(oblivious_salt, client_ctx->r, msg2->blind_salt); + debug(DEBUG, "Removing the blind from the oblivious salt"); + crypto_x25519_inverse(oblivious_salt, client->r, msg2->blind_salt); + print_point("obv_salt", oblivious_salt); // Hash the oblivious salt together with the id's to create the salt for the PHF uint8_t password_hash[64]; + debug(DEBUG, "Creating the salt for the PHF"); uint8_t phf_salt[32]; { crypto_blake2b_ctx hash_ctx; crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0); crypto_blake2b_update(&hash_ctx, oblivious_salt, 32); crypto_blake2b_update(&hash_ctx, - client_ctx->client_id, - client_ctx->client_id_len); + client->client_id, + client->client_id_len); crypto_blake2b_update(&hash_ctx, - client_ctx->server_id, - client_ctx->server_id_len); + client->server_id, + client->server_id_len); crypto_blake2b_final(&hash_ctx, phf_salt); } + print_point("phf_salt", phf_salt); + debug(DEBUG, "Running the PHF to generate p and v"); void *work_area; if ((work_area = malloc(msg2->phf_blocks * 1024)) == NULL) { + debug(ERROR, "Failed to malloc() memory for PHF work area"); return -1; } crypto_argon2i(password_hash, 64, work_area, msg2->phf_blocks, msg2->phf_iterations, - client_ctx->password, client_ctx->password_len, + client->password, client->password_len, phf_salt, 32); free(work_area); // p || v = pwKdf(password, BlindSalt, idC, idS, settings) uint8_t *p = &(password_hash[0]); uint8_t *v = &(password_hash[32]); + // In the setup protocol, we clamped v before using it + // So we should do the same here + crypto_x25519_clamp(v); + print_point("p", p); + print_point("v", v); // Hash p onto the curve to get this user's base point P uint8_t P[32]; + debug(DEBUG, "Hashing p onto the curve to get P"); crypto_hidden_to_curve(P, p); + print_point("P", P); // Generate a random ephemeral private key a, store it in ctx->a - arc4random_buf(client_ctx->a, 32); - crypto_x25519_clamp(client_ctx->a); + debug(DEBUG, "Generating ephemeral private key a"); + arc4random_buf(client->a, 32); + crypto_x25519_clamp(client->a); + print_point("a", client->a); // Generate the ephemeral public key A = a * P, store it in msg3->A - crypto_x25519_scalarmult(msg3->A, client_ctx->a, P, 256); + debug(DEBUG, "Generating ephemeral public key A = a * P"); + crypto_x25519_scalarmult(msg3->A, client->a, P, 256); + print_point("A", msg3->A); // Compute the two Diffie-Hellman shared secrets + debug(DEBUG, "Computing Diffie-Hellman shared secrets"); // DH shared secret from a * B uint8_t a_B[32]; - crypto_x25519(a_B, client_ctx->a, msg2->B); + crypto_x25519(a_B, client->a, msg2->B); + print_point("a * B", a_B); // DH shared secret from v * B uint8_t v_B[32]; crypto_x25519(v_B, v, msg2->B); + print_point("v * B", v_B); // Hash everything we know so far to generate our key, save it in ctx->K_c + debug(DEBUG, "Hashing current state to get key K_c"); { crypto_blake2b_ctx hash_ctx; crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0); crypto_blake2b_update(&hash_ctx, - client_ctx->client_id, - client_ctx->client_id_len); + client->client_id, + client->client_id_len); crypto_blake2b_update(&hash_ctx, - client_ctx->server_id, - client_ctx->server_id_len); + client->server_id, + client->server_id_len); crypto_blake2b_update(&hash_ctx, msg3->A, 32); crypto_blake2b_update(&hash_ctx, msg2->B, 32); crypto_blake2b_update(&hash_ctx, a_B, 32); crypto_blake2b_update(&hash_ctx, v_B, 32); - crypto_blake2b_final(&hash_ctx, client_ctx->K_c); + crypto_blake2b_final(&hash_ctx, client->K_c); } + print_point("K_c", client->K_c); // Hash k and the client modifier to get our verifier, save it in msg3->client_verifier + debug(DEBUG, "Hashing K_c and modifier to get our verifier"); { crypto_blake2b_ctx hash_ctx; crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0); - crypto_blake2b_update(&hash_ctx, client_ctx->K_c, 32); + crypto_blake2b_update(&hash_ctx, client->K_c, 32); crypto_blake2b_update(&hash_ctx, - BSSPEKE_VERIFY_CLIENT_MODIFIER, + (uint8_t *)BSSPEKE_VERIFY_CLIENT_MODIFIER, BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN); crypto_blake2b_final(&hash_ctx, msg3->client_verifier); } + print_point("client_v", msg3->client_verifier); return 0; } int -bsspeke_server_generate_message4(bsspeke_msg4_t *msg4, - const bsspeke_msg3_t *msg3, +bsspeke_server_login_generate_message4(bsspeke_login_msg4_t *msg4, + const bsspeke_login_msg3_t *msg3, bsspeke_server_ctx *server_ctx ) { // Compute the two Diffie-Hellman shared secrets @@ -257,7 +461,7 @@ bsspeke_server_generate_message4(bsspeke_msg4_t *msg4, crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0); crypto_blake2b_update(&hash_ctx, server_ctx->K_s, 32); crypto_blake2b_update(&hash_ctx, - BSSPEKE_VERIFY_CLIENT_MODIFIER, + (uint8_t *)BSSPEKE_VERIFY_CLIENT_MODIFIER, BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN); crypto_blake2b_final(&hash_ctx, my_client_verifier); } @@ -273,7 +477,7 @@ bsspeke_server_generate_message4(bsspeke_msg4_t *msg4, crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0); crypto_blake2b_update(&hash_ctx, server_ctx->K_s, 32); crypto_blake2b_update(&hash_ctx, - BSSPEKE_VERIFY_SERVER_MODIFIER, + (uint8_t *)BSSPEKE_VERIFY_SERVER_MODIFIER, BSSPEKE_VERIFY_SERVER_MODIFIER_LEN); crypto_blake2b_final(&hash_ctx, msg4->server_verifier); } @@ -283,7 +487,7 @@ bsspeke_server_generate_message4(bsspeke_msg4_t *msg4, } int -bsspeke_client_verify_message4(const bsspeke_msg4_t *msg4, +bsspeke_client_verify_message4(const bsspeke_login_msg4_t *msg4, const bsspeke_client_ctx *client_ctx ) { // Compute our own version of the server's verifier hash @@ -293,7 +497,7 @@ bsspeke_client_verify_message4(const bsspeke_msg4_t *msg4, crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0); crypto_blake2b_update(&hash_ctx, client_ctx->K_c, 32); crypto_blake2b_update(&hash_ctx, - BSSPEKE_VERIFY_SERVER_MODIFIER, + (uint8_t *)BSSPEKE_VERIFY_SERVER_MODIFIER, BSSPEKE_VERIFY_SERVER_MODIFIER_LEN); crypto_blake2b_final(&hash_ctx, my_server_verifier); } @@ -307,8 +511,3 @@ bsspeke_client_verify_message4(const bsspeke_msg4_t *msg4, return 0; } -int main(int argc, char *argv[]) -{ - - return 0; -} diff --git a/bsspeke.h b/bsspeke.h index 95f3a32f0c37331dbd638b9a6c121b90204c506e..8385b2afd21df55e4169a6efcd053ed1d1d343e5 100644 --- a/bsspeke.h +++ b/bsspeke.h @@ -51,42 +51,121 @@ typedef struct { } bsspeke_client_ctx; typedef struct { - uint8_t *server_id; + uint8_t *server_id; // Server's identifier (eg domain name) size_t server_id_len; - uint8_t *client_id; + + uint8_t *client_id; // Client's identifier (eg Matrix user_id) size_t client_id_len; - // Stored ECDH parameters for the given user - //uint8_t P[32]; // Base point + uint8_t P[32]; // Base point for the user uint8_t V[32]; // User's long-term public key - // Ephemeral keypair - uint8_t b[32]; - uint8_t B[32]; - // User's ephemeral public key - uint8_t A[32]; - // Session key - uint8_t K_s[32]; + + uint8_t b[32]; // Ephemeral private key + uint8_t B[32]; // Ephemeral public key + + uint8_t A[32]; // Client's ephemeral public key + + uint8_t K_s[32]; // Session key } bsspeke_server_ctx; typedef struct { char *client_id; - uint8_t *blind; + uint8_t blind[32]; } bsspeke_msg1_t; +typedef bsspeke_msg1_t bsspeke_setup_msg1_t; + +typedef struct { + uint8_t blind_salt[32]; +} bsspeke_setup_msg2_t; + +typedef struct { + uint8_t P[32]; + uint8_t V[32]; + uint32_t phf_blocks; + uint32_t phf_iterations; +} bsspeke_setup_msg3_t; + + +typedef bsspeke_msg1_t bsspeke_login_msg1_t; + typedef struct { uint8_t blind_salt[32]; uint8_t B[32]; uint32_t phf_blocks; uint32_t phf_iterations; -} bsspeke_msg2_t; +} bsspeke_login_msg2_t; typedef struct { uint8_t A[32]; uint8_t client_verifier[32]; -} bsspeke_msg3_t; +} bsspeke_login_msg3_t; typedef struct { uint8_t server_verifier[32]; -} bsspeke_msg4_t; +} bsspeke_login_msg4_t; + +void +bsspeke_client_init(bsspeke_client_ctx *ctx, + const char* client_id, const size_t client_id_len, + const char* server_id, const size_t server_id_len, + const char* password, const size_t password_len); + +void +bsspeke_server_init(bsspeke_server_ctx *ctx, + const char* server_id, const size_t server_id_len); + +void +bsspeke_client_setup_generate_message1 +( + bsspeke_setup_msg1_t *msg1, + bsspeke_client_ctx *client +); + + +void +bsspeke_server_setup_generate_message2 +( + bsspeke_setup_msg2_t *msg2, + const bsspeke_setup_msg1_t *msg1, + uint8_t *salt, const size_t salt_len, + bsspeke_server_ctx *server_ctx +); + +int +bsspeke_client_setup_generate_message3 +( + bsspeke_setup_msg3_t *msg3, + const bsspeke_setup_msg2_t *msg2, + uint32_t phf_blocks, uint32_t phf_iterations, + bsspeke_client_ctx *client +); + +void +bsspeke_client_login_generate_message1(bsspeke_login_msg1_t *msg1, + bsspeke_client_ctx *client); + +void +bsspeke_server_login_generate_message2(bsspeke_login_msg2_t *msg2, + const bsspeke_login_msg1_t *msg1, + const uint8_t *salt, const size_t salt_len, + uint8_t P[32], uint8_t V[32], + uint32_t phf_blocks, + uint32_t phf_iterations, + bsspeke_server_ctx *server_ctx); + +int +bsspeke_client_login_generate_message3(bsspeke_login_msg3_t *msg3, + const bsspeke_login_msg2_t *msg2, + bsspeke_client_ctx *client_ctx); + +int +bsspeke_server_login_generate_message4(bsspeke_login_msg4_t *msg4, + const bsspeke_login_msg3_t *msg3, + bsspeke_server_ctx *server_ctx); + +int +bsspeke_client_verify_message4(const bsspeke_login_msg4_t *msg4, + const bsspeke_client_ctx *client_ctx); #endif diff --git a/demo.c b/demo.c new file mode 100644 index 0000000000000000000000000000000000000000..c6d36f475a7c479dad1a547e990d3521f5eece9f --- /dev/null +++ b/demo.c @@ -0,0 +1,135 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "bsspeke.h" + +int demo_setup(uint8_t salt[32], + uint8_t P[32], + uint8_t V[32]) +{ + bsspeke_client_ctx client; + bsspeke_server_ctx server; + + puts(""); + puts("Starting Setup"); + + const char *client_id = "@alice:example.com"; + const char *server_id = "bob.example.com"; + const char *password = "P@ssword1"; + + puts(""); + puts("Setup: Initializing client"); + bsspeke_client_init(&client, + client_id, strlen(client_id), + server_id, strlen(server_id), + password, strlen(password)); + + puts("Setup: Initializing server"); + bsspeke_server_init(&server, + server_id, strlen(server_id)); + + bsspeke_setup_msg1_t msg1; + bsspeke_setup_msg2_t msg2; + bsspeke_setup_msg3_t msg3; + + puts(""); + puts("Setup: Client generating message 1"); + bsspeke_client_setup_generate_message1(&msg1, &client); + + puts(""); + puts("Setup: Server generating message 2"); + bsspeke_server_setup_generate_message2(&msg2, &msg1, + salt, 32, + &server); + + puts(""); + puts("Setup: Client generating message 3"); + bsspeke_client_setup_generate_message3(&msg3, &msg2, + 100000, 3, + &client); + + puts(""); + puts("Setup: Saving parameters from setup"); + memcpy(P, msg3.P, 32); + memcpy(V, msg3.V, 32); + + puts(""); + puts("Setup: Done"); + + return 0; +} + +int demo_login(uint8_t salt[32], + uint8_t P[32], + uint8_t V[32]) +{ + bsspeke_client_ctx client; + bsspeke_server_ctx server; + + puts(""); + puts("Starting Login"); + + const char *client_id = "@alice:example.com"; + const char *server_id = "bob.example.com"; + const char *password = "P@ssword1"; + + puts(""); + puts("Login: Initializing client"); + bsspeke_client_init(&client, + client_id, strlen(client_id), + server_id, strlen(server_id), + password, strlen(password)); + + puts("Login: Initializing server"); + bsspeke_server_init(&server, + server_id, strlen(server_id)); + + bsspeke_login_msg1_t msg1; + bsspeke_login_msg2_t msg2; + bsspeke_login_msg3_t msg3; + bsspeke_login_msg4_t msg4; + + puts(""); + puts("Login: Client generating message 1"); + bsspeke_client_login_generate_message1(&msg1, &client); + + puts(""); + puts("Login: Server generating message 2"); + bsspeke_server_login_generate_message2(&msg2, &msg1, + salt, 32, + P, V, + 100000, 3, + &server); + + puts(""); + puts("Login: Client generating message 3"); + bsspeke_client_login_generate_message3(&msg3, &msg2, &client); + + + return 0; + +} + +int main(int argc, char *argv[]) +{ + // Here we're pretending to be the server's long-term storage + // It needs to know the salt, V, and P for each registered user + uint8_t salt[32]; + uint8_t V[32]; + uint8_t P[32]; + + int rc = 0; + + if( (rc = demo_setup(salt, P, V)) != 0 ) { + puts("Setup failed :("); + exit(-1); + } + + if( (rc = demo_login(salt, P, V)) != 0 ) { + puts("Login failed :("); + exit(-1); + } + + return 0; +} diff --git a/doc/Login.md b/doc/Login.md new file mode 100644 index 0000000000000000000000000000000000000000..e07e0acf39d366ed5ba9fb1d503c16b624f29f08 --- /dev/null +++ b/doc/Login.md @@ -0,0 +1 @@ +# BS-SPEKE Login Protocol diff --git a/doc/Setup.md b/doc/Setup.md new file mode 100644 index 0000000000000000000000000000000000000000..b59e65a26234c358f3f0ae00caa696a658d57d8d --- /dev/null +++ b/doc/Setup.md @@ -0,0 +1,58 @@ +# BS-SPEKE Setup Protocol + +### Message 1 (client -> server) +This is the same as Message 1 in the login protocol. + +The client sends the user's ID and a blind for the OPRF. + +``` +client_id: string +blind: base64-encoded curve point +``` + +### Message 2 (server -> client) +This is similar to one half of the server's Message 2 in +the login protocol. + +The server multiplies its hashed salt by the client's blind +curve point to create the blind salt. + +``` +blind_salt: base64-encoded curve point +``` + +### Message 3 (client -> server) +Here the client finishes computation of the OPRF and returns +its public parameters to the server. + +``` +P: base64-encoded curve point +V: base64-encoded curve point +phf_params: { + name: string + blocks: integer + iterations: integer +} +``` + +There's a valid question here: +"How does the server know that these values come from the real +client, and not from a MITM?" + +The easy answer is that it's Trust On First Use (TOFU). + +A true MITM in the network would not be able to defeat the TLS +session that protects this connection for an application like Matrix. + +A malicious CDN could create arbitrary accounts, but they could +do that already with plaintext username and password. +Moreover, this reduces the server's vulnerability to the CDN; +now the adversary has to own the CDN at the moment when the password +is first set up. +Whereas before, owning the CDN at the time of any login was +sufficient to learn the user's password. +And here, the CDN (and the server) still NEVER learn the +password. +The most that the malicious CDN can do is to set their own fake +password for the victim user. +