Skip to content

Commit 1fa1141

Browse files
ayla-nordicsemirlubos
authored andcommitted
lib: app_jwt: add an app core jwt generator and add a sample for usage
Ref: NRFX-6688 Signed-off-by: Aymen LAOUINI <aymen.laouini@nordicsemi.no>
1 parent ac06821 commit 1fa1141

17 files changed

+1539
-0
lines changed

CODEOWNERS

+5
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@
192192
/doc/nrf/libraries/others/adp536x.rst @nrfconnect/ncs-cia-doc
193193
/doc/nrf/libraries/others/app_event_manager.rst @nrfconnect/ncs-si-muffin-doc @nrfconnect/ncs-si-bluebagel-doc
194194
/doc/nrf/libraries/others/app_event_manager_profiler_tracer.rst @nrfconnect/ncs-si-bluebagel-doc
195+
/doc/nrf/libraries/others/app_jwt.rst @nrfconnect/ncs-modem-doc @ayla-nordicsemi
195196
/doc/nrf/libraries/others/audio_module.rst @nrfconnect/ncs-audio-doc
196197
/doc/nrf/libraries/others/contin_array.rst @nrfconnect/ncs-audio-doc
197198
/doc/nrf/libraries/others/data_fifo.rst @nrfconnect/ncs-audio-doc
@@ -314,6 +315,7 @@
314315
/ext/iperf3/ @nrfconnect/ncs-code-owners @nrfconnect/ncs-modem-tre
315316

316317
# Include
318+
/include/app_jwt.h @nrfconnect/ncs-modem @ayla-nordicsemi
317319
/include/audio/ @nrfconnect/ncs-audio
318320
/include/audio_module/ @nrfconnect/ncs-audio
319321
/include/bluetooth/ @nrfconnect/ncs-dragoon
@@ -377,6 +379,7 @@
377379

378380
# Libraries
379381
/lib/adp536x/ @nrfconnect/ncs-cia
382+
/lib/app_jwt/ @nrfconnect/ncs-modem @ayla-nordicsemi
380383
/lib/at_cmd_parser/ @nrfconnect/ncs-co-networking @nrfconnect/ncs-modem
381384
/lib/at_cmd_custom/ @nrfconnect/ncs-modem
382385
/lib/at_host/ @nrfconnect/ncs-co-networking @nrfconnect/ncs-modem
@@ -439,6 +442,7 @@
439442
/samples/CMakeLists.txt @nrfconnect/ncs-co-build-system
440443
/samples/app_event_manager/ @nrfconnect/ncs-si-muffin @nrfconnect/ncs-si-bluebagel
441444
/samples/app_event_manager_profiler_tracer/ @nrfconnect/ncs-si-muffin @nrfconnect/ncs-si-bluebagel
445+
/samples/app_jwt/ @nrfconnect/ncs-modem @ayla-nordicsemi
442446
/samples/benchmarks/coremark/ @nrfconnect/ncs-si-bluebagel
443447
/samples/bluetooth/nrf_auraconfig/ @nrfconnect/ncs-audio
444448
/samples/bluetooth/central_and_peripheral_hr/ @nrfconnect/ncs-si-muffin
@@ -562,6 +566,7 @@
562566

563567
/samples/app_event_manager/*.rst @nrfconnect/ncs-si-muffin-doc @nrfconnect/ncs-si-bluebagel-doc
564568
/samples/app_event_manager_profiler_tracer/*.rst @nrfconnect/ncs-si-muffin-doc
569+
/samples/app_jwt/*.rst @nrfconnect/ncs-modem-doc @ayla-nordicsemi
565570
/samples/benchmarks/coremark/*.rst @nrfconnect/ncs-si-bluebagel-doc
566571
/samples/bluetooth/**/*.rst @nrfconnect/ncs-si-bluebagel-doc @nrfconnect/ncs-si-muffin-doc
567572
/samples/bluetooth/central_and_peripheral_hr/*.rst @nrfconnect/ncs-si-muffin-doc

doc/nrf/libraries/others/app_jwt.rst

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
.. _lib_app_jwt:
2+
3+
Application JWT
4+
###############
5+
6+
.. contents::
7+
:local:
8+
:depth: 2
9+
10+
The Application JWT library provides access to the `JSON Web Token (JWT)`_ generation feature from application core using signing and identity services from secure core.
11+
12+
Configuration
13+
*************
14+
15+
To use the library to request a JWT, complete the following steps:
16+
17+
1. Set the following Kconfig options to enable the library:
18+
19+
* :kconfig:option:`CONFIG_APP_JWT`
20+
* :kconfig:option:`CONFIG_APP_JWT_VERIFY_SIGNATURE`
21+
* :kconfig:option:`CONFIG_APP_JWT_PRINT_EXPORTED_PUBKEY_DER`
22+
* :kconfig:option:`CONFIG_NRF_SECURITY`
23+
* :Kconfig:option:`CONFIG_SSF_PSA_CRYPTO_SERVICE_ENABLED`
24+
* :Kconfig:option:`CONFIG_SSF_DEVICE_INFO_SERVICE_ENABLED`
25+
26+
#. Generate a signing key pair if you do not want to use the IAK Key.
27+
#. Populate the :c:struct:`app_jwt_data` structure with your desired values.
28+
See :ref:`app_jwt_values` for more information.
29+
#. Pass the structure to the function that generates JWT (:c:func:`app_jwt_generate`).
30+
31+
If the function executes successfully, :c:member:`app_jwt_data.jwt_buf` will contain the JSON web token.
32+
33+
.. note::
34+
If a timestamp is needed and there is an error getting the time from the clock source (or the returned time in seconds is 0), the **iat** field will contain the value set by the :kconfig:option:`CONFIG_APP_JWT_DEFAULT_TIMESTAMP` Kconfig option.
35+
36+
.. _app_jwt_values:
37+
38+
Possible structure values
39+
=========================
40+
41+
You can configure the following values in the :c:struct:`app_jwt_data` structure:
42+
43+
* :c:member:`app_jwt_data.sec_tag` - Optional, the ``sec_tag`` must contain a valid signing key.
44+
If set to ``0``, the library will use the IAK for signing.
45+
* :c:member:`app_jwt_data.key_type` - Required if ``sec_tag`` is not zero.
46+
Defines the type of key in the sec tag.
47+
* :c:member:`app_jwt_data.alg` - Required, always use the value ``JWT_ALG_TYPE_ES256``.
48+
Defines the JWT signing algorithm.
49+
Currently, only ECDSA 256 is supported.
50+
* :c:member:`app_jwt_data.add_keyid_to_header` - Optional.
51+
Corresponds to **kid** claim.
52+
Use ``false`` if you want to leave out this field.
53+
If filled with the value ``true``, the claim **kid** will contain the SHA256 of the DER of the public part of the signing key.
54+
* :c:member:`app_jwt_data.json_token_id` - Optional.
55+
Corresponds to **jti** claim.
56+
Use ``0`` if you want to leave out this field.
57+
* :c:member:`app_jwt_data.subject` - Optional.
58+
Corresponds to **sub** claim.
59+
Use ``0`` if you want to leave out this field.
60+
* :c:member:`app_jwt_data.audience` - Optional.
61+
Corresponds to **aud** claim.
62+
Use ``0`` if you want to leave out this field.
63+
* :c:member:`app_jwt_data.issuer` - Optional.
64+
Corresponds to **iss** claim.
65+
Use ``0`` if you want to leave out this field.
66+
* :c:member:`app_jwt_data.add_timestamp` - Optional.
67+
Corresponds to **iat** claim.
68+
Use ``false`` if you want to leave out this field.
69+
If filled with the value ``true``, the claim **iat** will be filled with the current timestamp in seconds.
70+
* :c:member:`app_jwt_data.validity_s` - Optional.
71+
Defines the expiration date for the JWT.
72+
If set to ``0``, the field **exp** will be omitted from the generated JWT.
73+
* :c:member:`app_jwt_data.jwt_buf` - Required.
74+
The buffer size must be from 600 to 900 bytes.
75+
You must provide a valid buffer.
76+
The library does not do any allocation.
77+
* :c:member:`app_jwt_data.jwt_sz` - Size of the JWT buffer.
78+
Required, must be equal to the size of :c:member:`app_jwt_data.jwt_buf`.
79+
80+
API documentation
81+
*****************
82+
83+
| Header file: :file:`include/app_jwt.h`
84+
| Source file: :file:`lib/app_jwt/app_jwt.c`
85+
86+
.. doxygengroup:: app_jwt

include/app_jwt.h

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#ifndef _APP_JWT_H
8+
#define _APP_JWT_H
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
/**
15+
* @file app_jwt.h
16+
*
17+
* @brief Generate a JWT with from application core.
18+
* @defgroup app_jwt JWT generation
19+
* @{
20+
*
21+
*/
22+
23+
#include <stdint.h>
24+
#include <stdbool.h>
25+
#include <strings.h>
26+
27+
/** @brief Maximum size of a JWT string, could be used to allocate JWT
28+
* output buffer.
29+
*/
30+
#define APP_JWT_STR_MAX_LEN 900
31+
32+
/** @brief Maximum valid duration for JWTs generated by user application */
33+
#define APP_JWT_VALID_TIME_S_MAX (7 * 24 * 60 * 60)
34+
35+
/** @brief Default valid duration for JWTs generated by user application */
36+
#define APP_JWT_VALID_TIME_S_DEF (10 * 60)
37+
38+
/** @brief UUID size in bytes */
39+
#define APP_JWT_UUID_BYTE_SZ 16
40+
41+
/** @brief UUID v4 format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + '\0' */
42+
#define APP_JWT_UUID_V4_STR_LEN (((APP_JWT_UUID_BYTE_SZ * 2) + 4) + 1)
43+
44+
/** @brief Size in bytes of each JWT String field */
45+
#define APP_JWT_CLAIM_MAX_SIZE 64
46+
47+
/** @brief The type of key to be used for signing the JWT. */
48+
enum app_jwt_key_type {
49+
JWT_KEY_TYPE_CLIENT_PRIV = 2,
50+
JWT_KEY_TYPE_ENDORSEMENT = 8,
51+
};
52+
53+
/** @brief JWT signing algorithm */
54+
enum app_jwt_alg_type {
55+
JWT_ALG_TYPE_ES256 = 0,
56+
};
57+
58+
/** @brief JWT parameters required for JWT generation and pointer to generated JWT */
59+
struct app_jwt_data {
60+
/** Sec tag to use for JWT signing */
61+
unsigned int sec_tag;
62+
/** Key type in the specified sec tag */
63+
enum app_jwt_key_type key_type;
64+
/** JWT signing algorithm */
65+
enum app_jwt_alg_type alg;
66+
67+
/**
68+
* Indicates if a 'kid' claim is required or not, if set to 1, 'kid' claim
69+
* will contain sha256 of the signing key.
70+
*/
71+
bool add_keyid_to_header;
72+
73+
/**
74+
* NULL terminated 'jti' claim; Unique identifier; can be used to prevent the
75+
* JWT from being replayed
76+
*/
77+
const char *json_token_id;
78+
/** NULL terminated 'sub' claim; the principal that is the subject of the JWT */
79+
const char *subject;
80+
/** NULL terminated 'aud' claim; intended recipient of the JWT */
81+
const char *audience;
82+
/** NULL terminated 'iss' claim; Issuer of the JWT */
83+
const char *issuer;
84+
85+
/**
86+
* Indicates if an issue timestamp is required or not, if set to 1, 'exp' claim
87+
* will be present.
88+
*/
89+
bool add_timestamp;
90+
91+
/**
92+
* Corresponds to 'exp' claim; Defines how long the JWT will be valid.
93+
* If application has a valid time source, and the 'iat' claim is present,
94+
* the timestamp in seconds will be added to this value.
95+
*/
96+
uint32_t validity_s;
97+
98+
/**
99+
* Buffer to which the NULL terminated JWT will be copied.
100+
* It is the responsibility of the user to provide a valid buffer.
101+
* The returned JWT could be as long as 900 bytes, use the
102+
* defined size value APP_JWT_STR_MAX_LEN to create your supplied return buffer.
103+
*/
104+
char *jwt_buf;
105+
/** Size of the user provided buffer. */
106+
size_t jwt_sz;
107+
};
108+
109+
/**
110+
* @brief Generate a JWT using the supplied parameters. If successful,
111+
* the JWT string will be stored in the supplied struct.
112+
* You are responsible for providing a valid pointer to store the JWT.
113+
*
114+
* Subject, audience, token ID and issuer fields may be NULL in which case those
115+
* fields are left out from generated JWT token.
116+
*
117+
* All fields will be truncated to 64 characters, you should always provide null
118+
* terminated strings.
119+
*
120+
* The API does not verify the time source validity, it is up to the caller to make sure
121+
* that the system has access to a valid time source, otherwise "iat" field will
122+
* contain an arbitrary timestamp.
123+
*
124+
* @param[in,out] jwt Pointer to struct containing JWT parameters and result.
125+
*
126+
* @retval 0 If the operation was successful.
127+
* @retval -errno Negative errno for other failures.
128+
*/
129+
int app_jwt_generate(struct app_jwt_data *const jwt);
130+
131+
/**
132+
* @brief Get the device UUID from the secure domain
133+
* and return it as a NULL terminated string in the supplied buffer.
134+
* The device UUID can be used as a device identifier for cloud services and
135+
* for secure device management using the nRF Cloud Identity Service.
136+
*
137+
* UUID v4 defined by ITU-T X.667 | ISO/IEC 9834-8 has a length of 35 bytes, add
138+
* 1 byte for the atring termination character. You are expected to provide a buffer
139+
* of at least 36 bytes.
140+
*
141+
* @param[out] uuid_buffer Pointer to buffer where the device UUID string will be written to.
142+
* @param[in] uuid_buffer_size Size of the provided buffer.
143+
*
144+
* @retval 0 If the operation was successful.
145+
* @retval -errno Negative errno for other failures.
146+
*/
147+
int app_jwt_get_uuid(char *uuid_buffer, const size_t uuid_buffer_size);
148+
149+
/** @} */
150+
151+
#ifdef __cplusplus
152+
}
153+
#endif
154+
155+
#endif /* _APP_JWT_H */

lib/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ add_subdirectory_ifdef(CONFIG_HW_ID_LIBRARY hw_id)
3333
add_subdirectory_ifdef(CONFIG_EDGE_IMPULSE edge_impulse)
3434
add_subdirectory_ifdef(CONFIG_WAVE_GEN_LIB wave_gen)
3535
add_subdirectory_ifdef(CONFIG_HW_UNIQUE_KEY_SRC hw_unique_key)
36+
add_subdirectory_ifdef(CONFIG_APP_JWT app_jwt)
3637
add_subdirectory_ifdef(CONFIG_MODEM_JWT modem_jwt)
3738
add_subdirectory_ifdef(CONFIG_MODEM_SLM modem_slm)
3839
add_subdirectory_ifdef(CONFIG_MODEM_ATTEST_TOKEN modem_attest_token)

lib/Kconfig

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
menu "Libraries"
88

9+
rsource "app_jwt/Kconfig"
910
rsource "bin/Kconfig"
1011
rsource "nrf_modem_lib/Kconfig"
1112
rsource "adp536x/Kconfig"

lib/app_jwt/CMakeLists.txt

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#
2+
# Copyright (c) 2024 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
zephyr_library()
8+
9+
zephyr_library_sources(
10+
app_jwt.c
11+
)

lib/app_jwt/Kconfig

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#
2+
# Copyright (c) 2024 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
menuconfig APP_JWT
8+
bool "Application JWT Library"
9+
depends on SSF_CLIENT && SSF_PSA_CRYPTO_SERVICE_ENABLED && SSF_DEVICE_INFO_SERVICE_ENABLED
10+
select BASE64
11+
# Needed for time and date
12+
select DATE_TIME
13+
select NET_SOCKETS
14+
select NETWORKING
15+
# Needed to print integer values in JSON
16+
select CJSON_LIB
17+
select CBPRINTF_FP_SUPPORT
18+
19+
if APP_JWT
20+
21+
config APP_JWT_DEFAULT_TIMESTAMP
22+
int "Default timestamp to use in case time value is 0"
23+
default 1735682400
24+
25+
config APP_JWT_VERIFY_SIGNATURE
26+
bool "Verify signature after signing"
27+
default y
28+
29+
config APP_JWT_PRINT_EXPORTED_PUBKEY_DER
30+
bool "Print to terminal the DER formatted public key"
31+
32+
module=APP_JWT
33+
module-str=User App JWT
34+
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"
35+
36+
endif # APP_JWT

0 commit comments

Comments
 (0)