Skip to content

Commit

Permalink
samples: crypto: Add sample for EC J-PAKE
Browse files Browse the repository at this point in the history
Adds sample demonstrating use of EC J-PAKE.

Signed-off-by: Vidar Lillebø <vidar.lillebo@nordicsemi.no>
  • Loading branch information
vili-nordic authored and rlubos committed Jun 22, 2023
1 parent 18e7c62 commit dfc74b9
Show file tree
Hide file tree
Showing 6 changed files with 329 additions and 0 deletions.
5 changes: 5 additions & 0 deletions doc/nrf/releases/release-notes-changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ Bluetooth mesh samples

* Fixed an issue with the sample not fitting into RAM size on the nrf52dk_nrf52832 board.

Cryptography samples
--------------------

* Added the :ref:`crypto_ecjpake` sample demonstrating usage of EC J-PAKE.

nRF9160 samples
---------------

Expand Down
6 changes: 6 additions & 0 deletions samples/crypto/ecjpake/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ecjpake)

target_sources(app PRIVATE src/main.c)
43 changes: 43 additions & 0 deletions samples/crypto/ecjpake/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.. _crypto_ecjpake:

Crypto: EC J-PAKE
#################

.. contents::
:local:
:depth: 2

The EC J-PAKE sample demonstrates how to do password-authenticated key exchange using
the elliptic curve (EC) version of the password-authenticated key exchange by
juggling (J-PAKE) protocol.

Requirements
************

The sample supports the following development kits:

.. table-from-sample-yaml::

Overview
********

The sample performs the following operations:

1. Initializes the Platform Security Architecture (PSA) API.
#. Goes through the steps for J-PAKE on server and client sides.
#. Verifies that the derived keys are the same.

Building and running
********************

.. |sample path| replace:: :file:`samples/crypto/ecjpake`


Testing
=======

After programming the sample to your development kit, complete the following steps to test it:

1. |connect_terminal|
#. Compile and program the application.
#. Observe the logs from the application using a terminal emulator.
9 changes: 9 additions & 0 deletions samples/crypto/ecjpake/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CONFIG_LOG=y
CONFIG_CONSOLE=y
CONFIG_PSA_CRYPTO_DRIVER_OBERON=y
CONFIG_PSA_WANT_ALG_TLS12_ECJPAKE_TO_PMS=y
CONFIG_NRF_SECURITY=y

CONFIG_MBEDTLS_ENABLE_HEAP=y
CONFIG_MBEDTLS_HEAP_SIZE=8192
CONFIG_MAIN_STACK_SIZE=8192
18 changes: 18 additions & 0 deletions samples/crypto/ecjpake/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
sample:
description: This app provides an example of EC J-PAKE
name: EC J-PAKE example
tests:
sample.ecjpake:
tags: introduction psa
platform_allow: >
nrf5340dk_nrf5340_cpuapp
nrf9160dk_nrf9160 nrf52840dk_nrf52840
harness: console
harness_config:
type: multi_line
regex:
- ".*Example finished successfully!.*"
integration_platforms:
- nrf5340dk_nrf5340_cpuapp
- nrf9160dk_nrf9160
- nrf52840dk_nrf52840
248 changes: 248 additions & 0 deletions samples/crypto/ecjpake/src/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#include <psa/crypto.h>
#include <stdint.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>
#include <zephyr/logging/log.h>
#include <zephyr/sys/util.h>

LOG_MODULE_REGISTER(ecjpake, LOG_LEVEL_DBG);

#define PRINT_HEX(p_label, p_text, len) \
({ \
LOG_INF("---- %s (len: %u): ----", p_label, len); \
LOG_HEXDUMP_INF(p_text, len, "Content:"); \
LOG_INF("---- %s end ----", p_label); \
})

#define APP_SUCCESS (0)
#define APP_ERROR (-1)
#define APP_SUCCESS_MESSAGE "Example finished successfully!"
#define APP_ERROR_MESSAGE "Example exited with error!"

psa_status_t do_rounds(psa_pake_operation_t *server, psa_pake_operation_t *client)
{
psa_pake_step_t round1_steps[] = {PSA_PAKE_STEP_KEY_SHARE, PSA_PAKE_STEP_ZK_PUBLIC,
PSA_PAKE_STEP_ZK_PROOF, PSA_PAKE_STEP_KEY_SHARE,
PSA_PAKE_STEP_ZK_PUBLIC, PSA_PAKE_STEP_ZK_PROOF};
uint8_t buffer[128];
size_t outlen;
psa_status_t status;

/* Server provides data to client */
for (uint32_t i = 0; i < ARRAY_SIZE(round1_steps); i++) {
status = psa_pake_output(server, round1_steps[i], buffer, sizeof(buffer), &outlen);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_output failed. (Error: %d)", status);
return status;
}

status = psa_pake_input(client, round1_steps[i], buffer, outlen);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_input failed. (Error: %d)", status);
return status;
}
}

/* Client provides data to server. */
for (uint32_t i = 0; i < ARRAY_SIZE(round1_steps); i++) {
status = psa_pake_output(client, round1_steps[i], buffer, sizeof(buffer), &outlen);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_output failed. (Error: %d)", status);
return status;
}

status = psa_pake_input(server, round1_steps[i], buffer, outlen);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_input failed. (Error: %d)", status);
return status;
}
}

psa_pake_step_t round2_steps[] = {PSA_PAKE_STEP_KEY_SHARE, PSA_PAKE_STEP_ZK_PUBLIC,
PSA_PAKE_STEP_ZK_PROOF};

/* Server provides data to client */
for (uint32_t i = 0; i < ARRAY_SIZE(round2_steps); i++) {
status = psa_pake_output(server, round2_steps[i], buffer, sizeof(buffer), &outlen);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_output failed. (Error: %d)", status);
return status;
}

status = psa_pake_input(client, round2_steps[i], buffer, outlen);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_input failed. (Error: %d)", status);
return status;
}
}

/* Client provides data to server. */
for (uint32_t i = 0; i < ARRAY_SIZE(round2_steps); i++) {
status = psa_pake_output(client, round2_steps[i], buffer, sizeof(buffer), &outlen);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_output failed. (Error: %d)", status);
return status;
}

status = psa_pake_input(server, round2_steps[i], buffer, outlen);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_input failed. (Error: %d)", status);
return status;
}
}

return status;
}

psa_status_t pake_setup(psa_pake_operation_t *op, psa_pake_cipher_suite_t *cs, const char *user,
const char *peer, psa_key_id_t *password)
{
psa_status_t status = psa_pake_setup(op, cs);

if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_setup failed. (Error: %d)", status);
return status;
}

status = psa_pake_set_user(op, user, strlen(user));
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_set_user failed. (Error: %d)", status);
return status;
}

status = psa_pake_set_peer(op, peer, strlen(peer));
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_set_peer failed. (Error: %d)", status);
return status;
}

status = psa_pake_set_password_key(op, *password);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_set_password_key failed. (Error: %d)", status);
return status;
}

return PSA_SUCCESS;
}

psa_status_t do_key_derivation(psa_pake_operation_t *op, uint8_t *key_buffer,
size_t key_buffer_size)
{
psa_key_derivation_operation_t kdf = PSA_KEY_DERIVATION_OPERATION_INIT;

psa_status_t status = psa_key_derivation_setup(&kdf, PSA_ALG_TLS12_ECJPAKE_TO_PMS);

if (status != PSA_SUCCESS) {
LOG_INF("psa_key_derivation_setup failed. (Error: %d)", status);
return status;
}

status = psa_pake_get_implicit_key(op, &kdf);
if (status != PSA_SUCCESS) {
LOG_INF("psa_pake_get_implicit_key failed. (Error: %d)", status);
psa_key_derivation_abort(&kdf);
return status;
}

status = psa_key_derivation_output_bytes(&kdf, key_buffer, key_buffer_size);
if (status != PSA_SUCCESS) {
LOG_INF("psa_key_derivation_output_bytes failed. (Error: %d)", status);
psa_key_derivation_abort(&kdf);
return status;
}

return PSA_SUCCESS;
}

int main(void)
{
psa_status_t status = psa_crypto_init();

if (status != PSA_SUCCESS) {
LOG_INF("psa_crypto_init failed. (Error: %d)", status);
}

psa_pake_cipher_suite_t cipher_suite = PSA_PAKE_CIPHER_SUITE_INIT;

psa_pake_cs_set_algorithm(&cipher_suite, PSA_ALG_JPAKE);
psa_pake_cs_set_primitive(&cipher_suite, PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC,
PSA_ECC_FAMILY_SECP_R1, 256));
psa_pake_cs_set_hash(&cipher_suite, PSA_ALG_SHA_256);

psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;

psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_DERIVE);
psa_set_key_algorithm(&key_attributes, PSA_ALG_JPAKE);
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_PASSWORD);

psa_key_id_t key;

status = psa_import_key(&key_attributes, "password", 8, &key);
if (status != PSA_SUCCESS) {
LOG_INF("psa_import_key failed. (Error: %d)", status);
goto error;
}

/* Initialize PAKE operation object for the client.*/
psa_pake_operation_t client = PSA_PAKE_OPERATION_INIT;

status = pake_setup(&client, &cipher_suite, "client", "server", &key);
if (status != PSA_SUCCESS) {
goto error;
}

/* Initialize PAKE operation object for the server. */
psa_pake_operation_t server = PSA_PAKE_OPERATION_INIT;

status = pake_setup(&server, &cipher_suite, "server", "client", &key);
if (status != PSA_SUCCESS) {
goto error;
}

/* Perform the data exchange rounds */
status = do_rounds(&server, &client);
if (status != PSA_SUCCESS) {
LOG_INF("EC J-PAKE rounds failed. (Error: %d)", status);
goto error;
}

/* Retrieve keys from J-PAKE results. */
uint8_t server_secret[32];
uint8_t client_secret[32];

do_key_derivation(&server, server_secret, sizeof(server_secret));
do_key_derivation(&client, client_secret, sizeof(client_secret));

PRINT_HEX("server_secret", server_secret, sizeof(server_secret));
PRINT_HEX("client_secret", client_secret, sizeof(client_secret));

bool compare_eq = true;

for (size_t i = 0; i < sizeof(server_secret); i++) {
if (server_secret[i] != client_secret[i]) {
compare_eq = false;
}
}

if (!compare_eq) {
LOG_ERR("Derived keys for server and client are not equal.");
goto error;
}

LOG_INF(APP_SUCCESS_MESSAGE);
return APP_SUCCESS;

error:
LOG_INF(APP_ERROR_MESSAGE);
psa_destroy_key(key);
psa_pake_abort(&client);
psa_pake_abort(&server);

return APP_ERROR;
}

0 comments on commit dfc74b9

Please sign in to comment.