/cpace

A CPace PAKE implementation using libsodium.

Primary LanguageCBSD 2-Clause "Simplified" LicenseBSD-2-Clause

CPace-Ristretto255, a balanced PAKE

A CPace implementation for libsodium 1.0.17+

Blurb

CPace is a protocol for two parties that share a low-entropy secret (password) to derive a strong shared key without disclosing the secret to offline dictionary attacks.

CPace is a balanced PAKE, meaning that both parties must know the low-entropy secret.

Applications include pairing IoT and mobile applications using ephemeral pin codes, QR-codes, serial numbers, etc.

Usage

The CPace protocol requires a single round trip.

It returns a set of two 256-bit (crypto_cpace_SHAREDKEYBYTES bytes) keys that can be used to communicate in both directions.

#include "crypto_cpace.h"

#include <sodium.h>

/* A client identifier (username, email address, public key...) */
#define CLIENT_ID "client"

/* A server identifier (IP address, host name, public key...) */
#define SERVER_ID "server"

/* The shared password */
#define PASSWORD "password"

/* Optional additional data (application name, version, time stamp...) */
#define ADDITIONAL_DATA "additional data"

int
main(void)
{
    crypto_cpace_state       ctx;
    crypto_cpace_shared_keys shared_keys_computed_by_client;
    crypto_cpace_shared_keys shared_keys_computed_by_server;
    unsigned char            public_data[crypto_cpace_PUBLICDATABYTES];
    unsigned char            response[crypto_cpace_RESPONSEBYTES];
    char                     hex[crypto_cpace_SHAREDKEYBYTES * 2 + 1];

    /* [BOTH SIDES] Initialize the library - This just calls sodium_init() */
    if (crypto_cpace_init() != 0) {
        return 1;
    }

    /* [CLIENT SIDE] Compute public data to be sent to the server */
    if (crypto_cpace_step1(&ctx, public_data, PASSWORD, sizeof PASSWORD - 1,
                           CLIENT_ID, sizeof CLIENT_ID - 1, SERVER_ID,
                           sizeof SERVER_ID,
                           (const unsigned char *) ADDITIONAL_DATA,
                           sizeof ADDITIONAL_DATA) != 0) {
        return 1;
    }

    /* [SERVER SIDE] Compute the shared keys using the public data,
     * and return a response to send back to the client.
     */
    if (crypto_cpace_step2(
            response, public_data, &shared_keys_computed_by_server, PASSWORD,
            sizeof PASSWORD - 1, CLIENT_ID, sizeof CLIENT_ID - 1, SERVER_ID,
            sizeof SERVER_ID, (const unsigned char *) ADDITIONAL_DATA,
            sizeof ADDITIONAL_DATA) != 0) {
        return 1;
    }

    /* [CLIENT SIDE] Compute the shared keys using the server response */
    if (crypto_cpace_step3(&ctx, &shared_keys_computed_by_client, response) !=
        0) {
        return 1;
    }

    /* Verification */

    printf("Client key computed by the client: %s\n",
           sodium_bin2hex(hex, sizeof hex,
                          shared_keys_computed_by_client.client_sk,
                          crypto_cpace_SHAREDKEYBYTES));
    printf("Client key computed by the server: %s\n",
           sodium_bin2hex(hex, sizeof hex,
                          shared_keys_computed_by_server.client_sk,
                          crypto_cpace_SHAREDKEYBYTES));
    printf("Server key computed by the client: %s\n",
           sodium_bin2hex(hex, sizeof hex,
                          shared_keys_computed_by_client.server_sk,
                          crypto_cpace_SHAREDKEYBYTES));
    printf("Server key computed by the server: %s\n",
           sodium_bin2hex(hex, sizeof hex,
                          shared_keys_computed_by_server.server_sk,
                          crypto_cpace_SHAREDKEYBYTES));
    return 0;
}

Notes

  • This implementation uses the Ristretto255 group and SHA-512 as the hash function, so that it can trivially be ported to wasm-crypto.
  • Client and server identifiers have a maximum size of 255 bytes.
  • A Rust version is available here.