Skip to content

Commit 04f4dcb

Browse files
AntonZmanordicjm
authored andcommitted
samples: crypto: Add ML-DSA signature verification sample
The sample includes a demonstration of a signature verification supported both by Oberon and CRACEN crypto drivers. Ref: NCSDK-40102 Signed-off-by: Anton Zyma <anton.zyma@nordicsemi.no>
1 parent f8d2ba9 commit 04f4dcb

7 files changed

Lines changed: 926 additions & 1 deletion

File tree

doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,10 @@ Cellular samples
387387
Cryptography samples
388388
--------------------
389389

390-
* Added support for the nRF54LC10A SoC (with and without TF-M) in the crypto samples.
390+
* Added:
391+
392+
* Support for the nRF54LC10A SoC (with and without TF-M) in the crypto samples.
393+
* The :ref:`crypto_ml_dsa` sample.
391394

392395
Debug samples
393396
-------------
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#
2+
# Copyright (c) 2026 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
cmake_minimum_required(VERSION 3.20.0)
8+
9+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
10+
11+
project(ml_dsa)
12+
13+
target_sources(app PRIVATE
14+
src/main.c
15+
)

samples/crypto/ml_dsa/README.rst

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
.. _crypto_ml_dsa:
2+
3+
Crypto: ML-DSA
4+
###############
5+
6+
.. contents::
7+
:local:
8+
:depth: 2
9+
10+
The ML-DSA sample demonstrates how to use the :ref:`PSA Crypto API <ug_psa_certified_api_overview_crypto>` to verify a message signature using the ML-DSA-65 post-quantum signature algorithm.
11+
12+
Requirements
13+
************
14+
15+
The sample supports the following development kits:
16+
17+
.. table-from-sample-yaml::
18+
19+
Overview
20+
********
21+
22+
The sample :ref:`enables PSA Crypto API <psa_crypto_support_enable>` and configures the following Kconfig options for the cryptographic features:
23+
24+
* :kconfig:option:`CONFIG_PSA_WANT_KEY_TYPE_ML_DSA_PUBLIC_KEY` - Used to enable support for the ML-DSA public key type from among the supported cryptographic operations for :ref:`ug_crypto_supported_features_key_types`.
25+
* :kconfig:option:`CONFIG_PSA_WANT_ALG_ML_DSA` - Used to enable support for the ML-DSA signature algorithm from among the supported cryptographic operations for :ref:`ug_crypto_supported_features_signature_algorithms`.
26+
* :kconfig:option:`CONFIG_PSA_WANT_ML_DSA_KEY_SIZE_65` - Used to enable support for the ML-DSA-65 key type.
27+
* :kconfig:option:`CONFIG_PSA_WANT_ALG_SHAKE128` and :kconfig:option:`CONFIG_PSA_WANT_ALG_SHAKE256` - Used to enable support for the XOF algorithms required internally by ML-DSA.
28+
29+
Once built and run, the sample performs the following operations:
30+
31+
1. Initialization:
32+
33+
a. The PSA Crypto API is initialized using the :c:func:`psa_crypto_init` function.
34+
#. A known ML-DSA-65 public key is imported using the :c:func:`psa_import_key` function.
35+
The public key is configured with usage flags for verification.
36+
The public key, the message, and the signature used in this sample are taken from a NIST ACVP ML-DSA signature verification test vector.
37+
38+
#. ML-DSA signature verification:
39+
40+
The signature is verified using the :c:func:`psa_verify_message` function with the imported public key.
41+
42+
#. Cleanup:
43+
44+
The public key is removed from the PSA crypto keystore using the :c:func:`psa_destroy_key` function.
45+
46+
Building and running
47+
********************
48+
49+
.. |sample path| replace:: :file:`samples/crypto/ml_dsa`
50+
51+
.. include:: /includes/build_and_run.txt
52+
53+
Testing
54+
=======
55+
56+
.. include:: /samples/crypto/aes_cbc/README.rst
57+
:start-after: crypto_sample_testing_start
58+
:end-before: crypto_sample_testing_end
59+
60+
.. code-block:: text
61+
62+
*** Booting nRF Connect SDK v3.4.99-baaa74699d09 ***
63+
*** Using Zephyr OS v4.4.0-bc35f6fe0b34 ***
64+
[00:00:00.003,144] <inf> ml_dsa: Starting ML-DSA example...
65+
[00:00:00.003,152] <inf> ml_dsa: Importing an ML-DSA-65 public key...
66+
[00:00:00.003,368] <inf> ml_dsa: ML-DSA-65 public key imported successfully!
67+
[00:00:00.003,383] <inf> ml_dsa: ---- ML-DSA-65 public key (total len: 1952, printing 16 bytes): ----
68+
[00:00:00.003,393] <inf> ml_dsa: Content:
69+
91 52 c0 c6 86 77 60 8f 42 6f b1 6f 8f 75 f7 6c |.R...w`. Bo.o.u.l
70+
[00:00:00.003,403] <inf> ml_dsa: ---- ML-DSA-65 public key end ----
71+
[00:00:00.003,409] <inf> ml_dsa: Verifying the ML-DSA signature...
72+
[00:00:00.138,565] <inf> ml_dsa: ---- Message (total len: 1554, printing 16 bytes): ----
73+
[00:00:00.138,578] <inf> ml_dsa: Content:
74+
88 45 ad 39 4b ce 60 b2 84 76 b2 13 99 b0 d4 72 |.E.9K.`. .v.....r
75+
[00:00:00.138,588] <inf> ml_dsa: ---- Message end ----
76+
[00:00:00.138,597] <inf> ml_dsa: ---- Signature (total len: 3309, printing 16 bytes): ----
77+
[00:00:00.138,606] <inf> ml_dsa: Content:
78+
6e 8c 4b 20 61 c2 cd f2 71 54 bf 70 85 f6 3c b1 |n.K a... qT.p..<.
79+
[00:00:00.138,615] <inf> ml_dsa: ---- Signature end ----
80+
[00:00:00.138,620] <inf> ml_dsa: Signature verification was successful!
81+
[00:00:00.138,730] <inf> ml_dsa: Example finished successfully!

samples/crypto/ml_dsa/prj.conf

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#
2+
# Copyright (c) 2026 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
# The Zephyr CMSIS emulation assumes that ticks are ms, currently
7+
CONFIG_SYS_CLOCK_TICKS_PER_SEC=1000
8+
9+
CONFIG_MAIN_STACK_SIZE=32768
10+
CONFIG_HEAP_MEM_POOL_SIZE=16384
11+
12+
# Enable logging
13+
CONFIG_CONSOLE=y
14+
CONFIG_LOG=y
15+
16+
CONFIG_SPEED_OPTIMIZATIONS=y
17+
18+
# Enable cryptographic features
19+
CONFIG_PSA_CRYPTO=y
20+
21+
CONFIG_PSA_CRYPTO_DRIVER_OBERON=n
22+
CONFIG_PSA_CRYPTO_DRIVER_CRACEN=y
23+
24+
CONFIG_MBEDTLS_ENABLE_HEAP=y
25+
CONFIG_MBEDTLS_HEAP_SIZE=16384
26+
27+
CONFIG_PSA_WANT_ALG_ML_DSA=y
28+
CONFIG_PSA_WANT_KEY_TYPE_ML_DSA_KEY_PAIR_IMPORT=y
29+
CONFIG_PSA_WANT_KEY_TYPE_ML_DSA_PUBLIC_KEY=y
30+
CONFIG_PSA_WANT_ML_DSA_KEY_SIZE_65=y
31+
32+
# Required by CRACEN to implement the ML-DSA algorithm
33+
CONFIG_PSA_WANT_ALG_SHAKE128=y
34+
CONFIG_PSA_WANT_ALG_SHAKE256=y

samples/crypto/ml_dsa/src/main.c

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* Copyright (c) 2026 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include <zephyr/sys/util.h>
8+
#include <zephyr/logging/log.h>
9+
#include <psa/crypto.h>
10+
#include <psa/crypto_extra.h>
11+
12+
#include "ml_dsa_65_vectors.h"
13+
14+
#define APP_SUCCESS (0)
15+
#define APP_ERROR (-1)
16+
#define APP_SUCCESS_MESSAGE "Example finished successfully!"
17+
#define APP_ERROR_MESSAGE "Example exited with error!"
18+
19+
/* ML-DSA signatures and keys are long, so printing is limited to just first bytes of these */
20+
#define PRINT_HEX_BYTE_LIMIT (16u)
21+
22+
#define PRINT_HEX(p_label, p_text, len)\
23+
({\
24+
size_t print_len = (size_t)(len);\
25+
size_t hex_bytes_to_print = MIN(print_len, (size_t)PRINT_HEX_BYTE_LIMIT); \
26+
LOG_INF("---- %s (total len: %zu, printing %zu bytes): ----", \
27+
p_label, print_len, hex_bytes_to_print); \
28+
LOG_HEXDUMP_INF(p_text, hex_bytes_to_print, "Content:"); \
29+
LOG_INF("---- %s end ----", p_label); \
30+
})
31+
32+
LOG_MODULE_REGISTER(ml_dsa, LOG_LEVEL_DBG);
33+
34+
/* ====================================================================== */
35+
/* Global variables/defines for the ML-DSA example */
36+
37+
static psa_key_id_t pub_key_id;
38+
/* ====================================================================== */
39+
40+
int crypto_init(void)
41+
{
42+
psa_status_t status;
43+
44+
/* Initialize PSA Crypto */
45+
status = psa_crypto_init();
46+
if (status != PSA_SUCCESS) {
47+
LOG_ERR("psa_crypto_init failed! (Error: %d)", status);
48+
return APP_ERROR;
49+
}
50+
51+
return APP_SUCCESS;
52+
}
53+
54+
int crypto_finish(void)
55+
{
56+
psa_status_t status;
57+
58+
/* Destroy the key handle */
59+
status = psa_destroy_key(pub_key_id);
60+
if (status != PSA_SUCCESS) {
61+
LOG_ERR("psa_destroy_key failed! (Error: %d)", status);
62+
return APP_ERROR;
63+
}
64+
65+
return APP_SUCCESS;
66+
}
67+
68+
int import_ml_dsa_pub_key(void)
69+
{
70+
/* Configure the key attributes */
71+
psa_key_attributes_t key_attributes = PSA_KEY_ATTRIBUTES_INIT;
72+
psa_status_t status;
73+
74+
LOG_INF("Importing an ML-DSA-65 public key...");
75+
76+
/* Configure the key attributes */
77+
psa_set_key_usage_flags(&key_attributes, PSA_KEY_USAGE_VERIFY_MESSAGE);
78+
psa_set_key_lifetime(&key_attributes, PSA_KEY_LIFETIME_VOLATILE);
79+
psa_set_key_algorithm(&key_attributes, PSA_ALG_ML_DSA);
80+
psa_set_key_type(&key_attributes, PSA_KEY_TYPE_ML_DSA_PUBLIC_KEY);
81+
82+
status = psa_import_key(&key_attributes, ml_dsa_65_pub_key, sizeof(ml_dsa_65_pub_key),
83+
&pub_key_id);
84+
if (status != PSA_SUCCESS) {
85+
LOG_ERR("psa_import_key failed! (Error: %d)", status);
86+
return APP_ERROR;
87+
}
88+
89+
LOG_INF("ML-DSA-65 public key imported successfully!");
90+
PRINT_HEX("ML-DSA-65 public key", ml_dsa_65_pub_key, sizeof(ml_dsa_65_pub_key));
91+
92+
/* Reset key attributes and free any allocated resources. */
93+
psa_reset_key_attributes(&key_attributes);
94+
95+
return APP_SUCCESS;
96+
}
97+
98+
int verify_message(void)
99+
{
100+
psa_status_t status;
101+
102+
LOG_INF("Verifying the ML-DSA signature...");
103+
104+
/* Verify the signature of the message */
105+
status = psa_verify_message(pub_key_id, PSA_ALG_ML_DSA, ml_dsa_65_message,
106+
sizeof(ml_dsa_65_message), ml_dsa_65_signature,
107+
sizeof(ml_dsa_65_signature));
108+
if (status != PSA_SUCCESS) {
109+
LOG_ERR("psa_verify_message failed! (Error: %d)", status);
110+
return APP_ERROR;
111+
}
112+
113+
PRINT_HEX("Message", ml_dsa_65_message, sizeof(ml_dsa_65_message));
114+
PRINT_HEX("Signature", ml_dsa_65_signature, sizeof(ml_dsa_65_signature));
115+
LOG_INF("Signature verification was successful!");
116+
117+
return APP_SUCCESS;
118+
}
119+
120+
int main(void)
121+
{
122+
int status;
123+
124+
LOG_INF("Starting ML-DSA example...");
125+
126+
status = crypto_init();
127+
if (status != APP_SUCCESS) {
128+
LOG_INF(APP_ERROR_MESSAGE);
129+
return APP_ERROR;
130+
}
131+
132+
status = import_ml_dsa_pub_key();
133+
if (status != APP_SUCCESS) {
134+
LOG_INF(APP_ERROR_MESSAGE);
135+
return APP_ERROR;
136+
}
137+
138+
status = verify_message();
139+
if (status != APP_SUCCESS) {
140+
LOG_INF(APP_ERROR_MESSAGE);
141+
(void)crypto_finish();
142+
return APP_ERROR;
143+
}
144+
145+
status = crypto_finish();
146+
if (status != APP_SUCCESS) {
147+
LOG_INF(APP_ERROR_MESSAGE);
148+
return APP_ERROR;
149+
}
150+
151+
LOG_INF(APP_SUCCESS_MESSAGE);
152+
153+
return APP_SUCCESS;
154+
}

0 commit comments

Comments
 (0)