Skip to content
Snippets Groups Projects
bsspeke.c 19.9 KiB
Newer Older
Charles Wright's avatar
Charles Wright committed
/*
 * bsspeke.c - BS-SPEKE over Curve25519
 *
 * Author: Charles V. Wright <cvwright@futo.org>
 * 
 * Copyright (c) 2022 FUTO Holdings, Inc.
Charles Wright's avatar
Charles Wright committed
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
Charles Wright's avatar
Charles Wright committed
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#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");
}

Charles Wright's avatar
Charles Wright committed
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
) {
    ctx->client_id = (uint8_t *)client_id;
    ctx->client_id_len = client_id_len;

    ctx->server_id = (uint8_t *)server_id;
    ctx->server_id_len = server_id_len;

    ctx->password = (uint8_t *)password;
    ctx->password_len = password_len;
}

void
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
Charles Wright's avatar
Charles Wright committed
) {
    debug(DEBUG, "Hashing client's password");
Charles Wright's avatar
Charles Wright committed
    // 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 hash_ctx;
Charles Wright's avatar
Charles Wright committed
        // Give us a 256 bit (32 byte) hash; Don't use a key
        crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
Charles Wright's avatar
Charles Wright committed
        // Add the client id, server id, and the password to the hash
        crypto_blake2b_update(&hash_ctx,
Charles Wright's avatar
Charles Wright committed
                              (const uint8_t *)(client->password),
                              client->password_len);
        crypto_blake2b_update(&hash_ctx,
Charles Wright's avatar
Charles Wright committed
                              (const uint8_t *)(client->client_id),
                              client->client_id_len);
        crypto_blake2b_update(&hash_ctx,
Charles Wright's avatar
Charles Wright committed
                              (const uint8_t *)(client->server_id),
                              client->server_id_len);
        // Write the digest value into `scalar_hash`
        crypto_blake2b_final(&hash_ctx, scalar_hash);
Charles Wright's avatar
Charles Wright committed
    }
    debug(DEBUG, "Mapping password hash onto the curve");
Charles Wright's avatar
Charles Wright committed
    // 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);

Charles Wright's avatar
Charles Wright committed
    // 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`");
Charles Wright's avatar
Charles Wright committed
    arc4random_buf(client->r, 32);
    print_point("r", client->r);
    debug(DEBUG, "Clamping r");
Charles Wright's avatar
Charles Wright committed
    crypto_x25519_clamp(client->r);
    print_point("r", client->r);

    debug(DEBUG, "Multiplying curve point by r");
Charles Wright's avatar
Charles Wright committed
    // 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");

    msg1->client_id = client->client_id;
Charles Wright's avatar
Charles Wright committed
    return;
}

void
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_user_info_t *user_info,
        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");
    user_info->salt_len = 32;
    arc4random_buf(user_info->salt, user_info->salt_len);
    print_point("salt", user_info->salt);

    // Hash the salt
    debug(DEBUG, "Hashing the salt");
    uint8_t H_salt[32];
    crypto_blake2b_general(H_salt, 32,
                           NULL, 0,
                           user_info->salt,
                           user_info->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);

    // Send our PHF params so the server can store them for us
    msg3->phf_blocks = phf_blocks;
    msg3->phf_iterations = phf_iterations;

void
bsspeke_server_setup_process_message3
    (
        bsspeke_setup_msg3_t *msg3,
        bsspeke_user_info_t *user_info,
        bsspeke_server_ctx *server
    )
{
    // Setup Message 3 contains stuff that we need to remember.
    // The user info struct is where we store stuff that
    // needs to persist across sessions.
    memcpy(user_info->P, msg3->P, 32);
    memcpy(user_info->V, msg3->V, 32);
    user_info->phf_blocks = msg3->phf_blocks;
    user_info->phf_iterations = msg3->phf_iterations;
}

void
bsspeke_client_login_generate_message1
(
    bsspeke_login_msg1_t *msg1,
    bsspeke_client_ctx *client
Charles Wright's avatar
Charles Wright committed
) {
    bsspeke_client_generate_message1(msg1, client);
Charles Wright's avatar
Charles Wright committed
}

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,
                                 const bsspeke_user_info_t *user_info,
                                 bsspeke_server_ctx *server
Charles Wright's avatar
Charles Wright committed
) {
    // Record the client's id
    server->client_id_len = strlen(msg1->client_id);
    server->client_id = (uint8_t *)malloc(server->client_id_len + 1);
    strncpy(server->client_id, msg1->client_id, server->client_id_len);
    printf("Server got msg1 from [%s]\n", (char *)(server->client_id));

Charles Wright's avatar
Charles Wright committed
    // Hash the salt
    debug(DEBUG, "Hashing the salt");
Charles Wright's avatar
Charles Wright committed
    uint8_t H_salt[32];
    crypto_blake2b_general(H_salt, 32,
                           NULL, 0,
                           user_info->salt,
                           user_info->salt_len);
Charles Wright's avatar
Charles Wright committed
    // Use clamp() to ensure we stay on the curve in the multiply below
    crypto_x25519_clamp(H_salt);
    print_point("H_salt", H_salt);
Charles Wright's avatar
Charles Wright committed
    // Multiply H(salt) by msg1->blind, save into msg2->blind_salt
    debug(DEBUG, "Multiplying H_salt by the user's blind");
Charles Wright's avatar
Charles Wright committed
    crypto_x25519_scalarmult(msg2->blind_salt, H_salt, msg1->blind, 256);
    print_point("blndsalt", msg2->blind_salt);
Charles Wright's avatar
Charles Wright committed

    // Generate random ephemeral private key b, save it in 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 user's base point P");
    print_point("P", user_info->P);
Charles Wright's avatar
Charles Wright committed

    // Compute public key B = b * P, save it in msg2->B
    debug(DEBUG, "Computing ephemeral public key B = b * P");
    crypto_x25519_scalarmult(server->B, server->b, user_info->P, 256);
    print_point("B", server->B);
Charles Wright's avatar
Charles Wright committed

    // Copy the public key into the outgoing message as well
    memcpy(msg2->B, server->B, 32);
Charles Wright's avatar
Charles Wright committed

    // Copy the PHF params too
    msg2->phf_blocks = user_info->phf_blocks;
    msg2->phf_iterations = user_info->phf_iterations;
Charles Wright's avatar
Charles Wright committed
int
bsspeke_client_login_generate_message3(bsspeke_login_msg3_t *msg3,
                                 const bsspeke_login_msg2_t *msg2,
                                 bsspeke_client_ctx *client
Charles Wright's avatar
Charles Wright committed
) {
    // Sanity checks first, before we do any work
    if( msg2->phf_blocks < BSSPEKE_MIN_PHF_BLOCKS ) {
        debug(ERROR, "phf_blocks is too low.  Aborting.");
Charles Wright's avatar
Charles Wright committed
        return -1;
    }
    if( msg2->phf_iterations < BSSPEKE_MIN_PHF_ITERATIONS ) {
        debug(ERROR, "phf_iterations is too low.  Aborting.");
Charles Wright's avatar
Charles Wright committed
        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);
Charles Wright's avatar
Charles Wright committed

    // 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");
Charles Wright's avatar
Charles Wright committed
    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);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx,
                              client->server_id,
                              client->server_id_len);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_final(&hash_ctx, phf_salt);
    }
    print_point("phf_salt", phf_salt);
Charles Wright's avatar
Charles Wright committed

    debug(DEBUG, "Running the PHF to generate p and v");
Charles Wright's avatar
Charles Wright committed
    void *work_area;
    if ((work_area = malloc(msg2->phf_blocks * 1024)) == NULL) {
        debug(ERROR, "Failed to malloc() memory for PHF work area");
Charles Wright's avatar
Charles Wright committed
        return -1;
    }
    crypto_argon2i(password_hash, 64, work_area,
                   msg2->phf_blocks, msg2->phf_iterations,
                   client->password, client->password_len,
Charles Wright's avatar
Charles Wright committed
                   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);
Charles Wright's avatar
Charles Wright committed

    // 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");
Charles Wright's avatar
Charles Wright committed
    crypto_hidden_to_curve(P, p);
    print_point("P", P);
Charles Wright's avatar
Charles Wright committed

    // Generate a random ephemeral private key a, store it in ctx->a
    debug(DEBUG, "Generating ephemeral private key a");
    arc4random_buf(client->a, 32);
    crypto_x25519_clamp(client->a);
    print_point("a", client->a);
Charles Wright's avatar
Charles Wright committed
    // Generate the ephemeral public key A = a * P, store it in msg3->A
    debug(DEBUG, "Generating ephemeral public key A = a * P");
    crypto_x25519_scalarmult(msg3->A, client->a, P, 256);
    print_point("A", msg3->A);
Charles Wright's avatar
Charles Wright committed

    // Compute the two Diffie-Hellman shared secrets 
    debug(DEBUG, "Computing Diffie-Hellman shared secrets");
Charles Wright's avatar
Charles Wright committed
    // DH shared secret from a * B
    uint8_t a_B[32];
    crypto_x25519(a_B, client->a, msg2->B);
    print_point("a * B", a_B);
Charles Wright's avatar
Charles Wright committed
    // DH shared secret from v * B
    uint8_t v_B[32];
    crypto_x25519(v_B, v, msg2->B);
    print_point("v * B", v_B);
Charles Wright's avatar
Charles Wright committed

    // 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");
Charles Wright's avatar
Charles Wright committed
    {
        crypto_blake2b_ctx hash_ctx;
        crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
        crypto_blake2b_update(&hash_ctx,
                              client->client_id,
                              client->client_id_len);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx,
                              client->server_id,
                              client->server_id_len);
Charles Wright's avatar
Charles Wright committed
        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->K_c);
Charles Wright's avatar
Charles Wright committed
    }
    print_point("K_c", client->K_c);
Charles Wright's avatar
Charles Wright committed

    // 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");
Charles Wright's avatar
Charles Wright committed
    {
        crypto_blake2b_ctx hash_ctx;
        crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
        crypto_blake2b_update(&hash_ctx, client->K_c, 32);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx,
                              (uint8_t *)BSSPEKE_VERIFY_CLIENT_MODIFIER,
Charles Wright's avatar
Charles Wright committed
                              BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN);
        crypto_blake2b_final(&hash_ctx, msg3->client_verifier);
    }
    print_point("client_v", msg3->client_verifier);
Charles Wright's avatar
Charles Wright committed

    return 0;
}

int
bsspeke_server_login_generate_message4(bsspeke_login_msg4_t *msg4,
                                 const bsspeke_login_msg3_t *msg3,
                                 const bsspeke_user_info_t *user_info,
                                 bsspeke_server_ctx *server
Charles Wright's avatar
Charles Wright committed
) {
    // Compute the two Diffie-Hellman shared secrets
    debug(DEBUG, "Computing Diffie-Hellman shared secrets");
Charles Wright's avatar
Charles Wright committed
    // DH shared secret from b * A
    uint8_t b_A[32];
    crypto_x25519(b_A, server->b, msg3->A);
    print_point("b * A", b_A);
Charles Wright's avatar
Charles Wright committed
    // DH shared secret from b * V
    //print_point("V", user_info->V);
Charles Wright's avatar
Charles Wright committed
    uint8_t b_V[32];
    crypto_x25519(b_V, server->b, user_info->V);
    print_point("b * V", b_V);
Charles Wright's avatar
Charles Wright committed

    // Hash everything we've learned so far to generate k, save it in ctx->k
    debug(DEBUG, "Hashing state so far to generate K_s");
Charles Wright's avatar
Charles Wright committed
    {
        crypto_blake2b_ctx hash_ctx;
        crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
        crypto_blake2b_update(&hash_ctx,
                              server->client_id,
                              server->client_id_len);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx,
                              server->server_id,
                              server->server_id_len);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx, msg3->A, 32);
        crypto_blake2b_update(&hash_ctx, server->B, 32);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx, b_A, 32);
        crypto_blake2b_update(&hash_ctx, b_V, 32);
        crypto_blake2b_final(&hash_ctx, server->K_s);
Charles Wright's avatar
Charles Wright committed
    }
    print_point("K_s", server->K_s);
Charles Wright's avatar
Charles Wright committed

    // Check that the client's hash is correct
    // Compute H( k || VERIFY_CLIENT_MODIFIER )
    debug(DEBUG, "Checking client's verifier hash");
Charles Wright's avatar
Charles Wright committed
    uint8_t my_client_verifier[32];
    {
        crypto_blake2b_ctx hash_ctx;
        crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
        crypto_blake2b_update(&hash_ctx, server->K_s, 32);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx,
                              (uint8_t *)BSSPEKE_VERIFY_CLIENT_MODIFIER,
Charles Wright's avatar
Charles Wright committed
                              BSSPEKE_VERIFY_CLIENT_MODIFIER_LEN);
        crypto_blake2b_final(&hash_ctx, my_client_verifier);
    }
    print_point("client's", msg3->client_verifier);
    print_point("mine", my_client_verifier);
Charles Wright's avatar
Charles Wright committed

    // Compare vs msg3->client_verifier
    if( crypto_verify32(msg3->client_verifier, my_client_verifier) != 0 ) {
        debug(ERROR, "Client's verifier doesn't match!");
Charles Wright's avatar
Charles Wright committed
        return -1;
    }
    debug(DEBUG, "Client's verifier checks out");
Charles Wright's avatar
Charles Wright committed

    // Compute our own verifier H( k || VERIFY_SERVER_MODIFIER ), save it in msg4->server_verifier
    debug(DEBUG, "Computing server verifier hash");
Charles Wright's avatar
Charles Wright committed
    {
        crypto_blake2b_ctx hash_ctx;
        crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
        crypto_blake2b_update(&hash_ctx, server->K_s, 32);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx,
                              (uint8_t *)BSSPEKE_VERIFY_SERVER_MODIFIER,
Charles Wright's avatar
Charles Wright committed
                              BSSPEKE_VERIFY_SERVER_MODIFIER_LEN);
        crypto_blake2b_final(&hash_ctx, msg4->server_verifier);
    }
    print_point("server_v", msg4->server_verifier);
Charles Wright's avatar
Charles Wright committed

    // If we made it this far, return success
    return 0;
}

int
bsspeke_client_login_verify_message4(const bsspeke_login_msg4_t *msg4,
                               const bsspeke_client_ctx *client
Charles Wright's avatar
Charles Wright committed
) {
    // Compute our own version of the server's verifier hash
    debug(DEBUG, "Verifying hash from the server");
Charles Wright's avatar
Charles Wright committed
    uint8_t my_server_verifier[32];
    {
        crypto_blake2b_ctx hash_ctx;
        crypto_blake2b_general_init(&hash_ctx, 32, NULL, 0);
        crypto_blake2b_update(&hash_ctx, client->K_c, 32);
Charles Wright's avatar
Charles Wright committed
        crypto_blake2b_update(&hash_ctx,
                              (uint8_t *)BSSPEKE_VERIFY_SERVER_MODIFIER,
Charles Wright's avatar
Charles Wright committed
                              BSSPEKE_VERIFY_SERVER_MODIFIER_LEN);
        crypto_blake2b_final(&hash_ctx, my_server_verifier);
    }
    print_point("mine", my_server_verifier);
    print_point("server's", msg4->server_verifier);
Charles Wright's avatar
Charles Wright committed

    // If the hashes don't match, return failure
    if( crypto_verify32(msg4->server_verifier, my_server_verifier) != 0 ) {
        debug(WARN, "Server's hash doesn't match.  Aborting.");
Charles Wright's avatar
Charles Wright committed
        return -1;
    }
    debug(DEBUG, "Server's hash checks out.  SUCCESS!");
Charles Wright's avatar
Charles Wright committed

    // Otherwise, return success
    return 0;
}