Skip to content

Commit

Permalink
lib: app_jwt: add an app core jwt generator and add a sample for usage
Browse files Browse the repository at this point in the history
Ref: NRFX-6688

Signed-off-by: Aymen LAOUINI <aymen.laouini@nordicsemi.no>
  • Loading branch information
ayla-nordicsemi committed Feb 24, 2025
1 parent 11f3037 commit 9d95b0b
Show file tree
Hide file tree
Showing 18 changed files with 1,368 additions and 0 deletions.
5 changes: 5 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
/doc/nrf/includes/ @nrfconnect/ncs-doc-leads
/doc/nrf/includes/boardname_tables/sample_boardnames.txt @nrfconnect/ncs-co-doc
/doc/nrf/installation/ @nrfconnect/ncs-doc-leads @nrfconnect/ncs-vestavind-doc @nrfconnect/ncs-wayland-doc
/doc/nrf/libraries/app_jwt/ @nrfconnect/ncs-modem-doc @ayla-nordicsemi
/doc/nrf/libraries/bin/ @nrfconnect/ncs-doc-leads
/doc/nrf/libraries/bin/lwm2m_carrier/ @nrfconnect/ncs-carrier-doc
/doc/nrf/libraries/bluetooth/ @nrfconnect/ncs-si-muffin-doc @nrfconnect/ncs-dragoon-doc
Expand Down Expand Up @@ -315,6 +316,7 @@
/ext/iperf3/ @nrfconnect/ncs-code-owners @jhirsi

# Include
/include/app_jwt.h @nrfconnect/ncs-modem @ayla-nordicsemi
/include/audio/ @nrfconnect/ncs-audio
/include/audio_module/ @nrfconnect/ncs-audio
/include/bluetooth/ @nrfconnect/ncs-dragoon
Expand Down Expand Up @@ -378,6 +380,7 @@

# Libraries
/lib/adp536x/ @nrfconnect/ncs-cia
/lib/app_jwt/ @nrfconnect/ncs-modem @ayla-nordicsemi
/lib/at_cmd_parser/ @nrfconnect/ncs-co-networking @nrfconnect/ncs-modem
/lib/at_cmd_custom/ @nrfconnect/ncs-modem
/lib/at_host/ @nrfconnect/ncs-co-networking @nrfconnect/ncs-modem
Expand Down Expand Up @@ -519,6 +522,7 @@
/samples/gazell/ @leewkb4567
/samples/hw_id/ @nrfconnect/ncs-cia
/samples/ipc/ipc_service/ @nrfconnect/ncs-si-muffin
/samples/jwt/ @nrfconnect/ncs-modem @ayla-nordicsemi
/samples/keys/ @nrfconnect/ncs-aegir
/samples/matter/ @nrfconnect/ncs-matter
/samples/mpsl/ @nrfconnect/ncs-dragoon
Expand Down Expand Up @@ -638,6 +642,7 @@
/samples/gazell/**/*.rst @nrfconnect/ncs-si-muffin-doc
/samples/hw_id/*.rst @nrfconnect/ncs-cia-doc
/samples/ipc/ipc_service/*.rst @nrfconnect/ncs-si-muffin-doc
/samples/jwt/*.rst @nrfconnect/ncs-modem-doc @ayla-nordicsemi
/samples/keys/**/*.rst @nrfconnect/ncs-aegir-doc
/samples/matter/**/*.rst @nrfconnect/ncs-matter-doc
/samples/mpsl/**/*.rst @nrfconnect/ncs-dragoon-doc
Expand Down
80 changes: 80 additions & 0 deletions doc/nrf/libraries/app_jwt/app_jwt.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
.. _app_jwt:

Application JWT
###################

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.

Configuration
*************

To use the library to request a JWT, complete the following steps:

1. Set the following Kconfig options to enable the library:

* :kconfig:option:`CONFIG_APP_JWT`
* :kconfig:option:`CONFIG_APP_JWT_VERIFY_SIGNATURE`
* :kconfig:option:`CONFIG_APP_JWT_PRINT_EXPORTED_PUBKEY_DER`
* :kconfig:option:`CONFIG_NRF_SECURITY`
* :Kconfig:option:`CONFIG_SSF_PSA_CRYPTO_SERVICE_ENABLED`
* :Kconfig:option:`CONFIG_SSF_DEVICE_INFO_SERVICE_ENABLED`

#. Read the device UUID (:c:func:`app_jwt_get_uuid`)
#. Populate the :c:struct:`app_jwt_data` structure with your desired values.
See `Possible structure values`_ for more information.
#. Pass the structure to the function that generates JWT (:c:func:`app_jwt_generate`).

If the function executes successfully, :c:member:`app_jwt_data.jwt_buf` will contain the JSON Web Token.

.. note::
The library doesn't check the validity of the time source, it is up to the caller to make sure that the system has access to a valid one, otherwise "iat" field will containt the time since boot in seconds.

.. note::
If the time value in seconds is equal to ``0``, the claim ``iat`` will be left out of the JWT.

Possible structure values
=========================

You can configure the following values in the :c:struct:`app_jwt_data` structure:

* :c:member:`app_jwt_data.sec_tag` - Optional, the ``sec_tag`` must contain a valid signing key.
If set to 0, the library will use the IAK for signing.
* :c:member:`app_jwt_data.key_type` - Required if ``sec_tag`` is not zero.
Defines the type of key in the sec tag.
* :c:member:`app_jwt_data.alg` - Required, always use the value JWT_ALG_TYPE_ES256.
Defines the JWT signing algorithm. Currently, only ECDSA 256 is supported.
* :c:member:`app_jwt_data.add_keyid_to_header` - Optional.
Corresponds to ``keyid`` claim.
Use ``0`` if you want to leave out this field.
* :c:member:`app_jwt_data.json_token_id` - Optional.
Corresponds to ``jti`` claim.
Use ``0`` if you want to leave out this field.
* :c:member:`app_jwt_data.subject` - Optional.
Corresponds to ``sub`` claim.
Use ``0`` if you want to leave out this field.
* :c:member:`app_jwt_data.audience` - Optional.
Corresponds to ``aud`` claim.
Use ``0`` if you want to leave out this field.
* :c:member:`app_jwt_data.issuer` - Optional.
Corresponds to ``iss`` claim.
Use ``0`` if you want to leave out this field.
* :c:member:`app_jwt_data.add_timestamp` - Optional.
Corresponds to ``iat`` claim.
Use ``0`` if you want to leave out this field.
* :c:member:`app_jwt_data.validity_s` - Optional.
Defines the expiration date for the JWT.
If set to 0, the field ``exp`` will be omitted from the generated JWT.
* :c:member:`app_jwt_data.jwt_buf` - Required.
Buffer for the generated, null-terminated, JWT string.
Buffer size has to be al least 600 bytes, at most 900 bytes.
The user has to provide a valid buffer, library doesn't do any allocation.
* :c:member:`app_jwt_data.jwt_sz` - Size of JWT buffer.
Required, has to be equal to the size of :c:member:`app_jwt_data.jwt_buf`.

API documentation
*****************

| Header file: :file:`include/app_jwt.h`
| Source file: :file:`lib/app_jwt/app_jwt.c`
.. doxygengroup:: app_jwt
11 changes: 11 additions & 0 deletions doc/nrf/libraries/app_jwt/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.. _lib_app_jwt:

Library Application JWT
########################

.. toctree::
:maxdepth: 1
:glob:
:caption: Subpages:

*
155 changes: 155 additions & 0 deletions include/app_jwt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
*/

#ifndef _APP_JWT_H
#define _APP_JWT_H

#ifdef __cplusplus
extern "C" {
#endif

/**
* @file app_jwt.h
*
* @brief Generate a JWT with from application core.
* @defgroup app_jwt JWT generation
* @{
*
*/

#include <stdint.h>
#include <stdbool.h>
#include <strings.h>

/** @brief Maximum size of a JWT string, could be used to allocate JWT
* output buffer.
*/
#define APP_JWT_STR_MAX_LEN 900

/** @brief Maximum valid duration for JWTs generated by user application */
#define APP_JWT_VALID_TIME_S_MAX (7 * 24 * 60 * 60)

/** @brief Default valid duration for JWTs generated by user application */
#define APP_JWT_VALID_TIME_S_DEF (10 * 60)

/** @brief UUID size in bytes */
#define APP_JWT_UUID_BYTE_SZ 16

/** @brief UUID v4 format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + '\0' */
#define APP_JWT_UUID_V4_STR_LEN (((APP_JWT_UUID_BYTE_SZ * 2) + 4) + 1)

/** @brief Size in bytes of each JWT String field */
#define APP_JWT_CLAIM_MAX_SIZE 64

/** @brief The type of key to be used for signing the JWT. */
enum app_jwt_key_type {
JWT_KEY_TYPE_CLIENT_PRIV = 2,
JWT_KEY_TYPE_ENDORSEMENT = 8,
};

/** @brief JWT signing algorithm */
enum app_jwt_alg_type {
JWT_ALG_TYPE_ES256 = 0,
};

/** @brief JWT parameters required for JWT generation and pointer to generated JWT */
struct app_jwt_data {
/** Sec tag to use for JWT signing */
unsigned int sec_tag;
/** Key type in the specified sec tag */
enum app_jwt_key_type key_type;
/** JWT signing algorithm */
enum app_jwt_alg_type alg;

/**
* Indicates if a 'kid' claim is requiered or not, if set to 1, 'kid' claim
* will contain sha256 of the signing key.
*/
bool add_keyid_to_header;

/**
* NULL terminated 'jti' claim; Unique identifier; can be used to prevent the
* JWT from being replayed
*/
const char *json_token_id;
/** NULL terminated 'sub' claim; the principal that is the subject of the JWT */
const char *subject;
/** NULL terminated 'aud' claim; intended recipient of the JWT */
const char *audience;
/** NULL terminated 'iss' claim; Issuer of the JWT */
const char *issuer;

/**
* Indicates if an issue timestamp is requiered or not, if set to 1, 'exp' claim
* will be present.
*/
bool add_timestamp;

/**
* Corresponds to 'exp' claim; Defines how long the JWT will be valid.
* If application has a valid time source, and the 'iat' claim is present,
* the timestamp in seconds will be added to this value.
*/
uint32_t validity_s;

/**
* Buffer to which the NULL terminated JWT will be copied.
* It is the responsibility of the user to provide a valid buffer.
* The returned JWT could be as long as 900 bytes, use the
* defined size value APP_JWT_STR_MAX_LEN to create your supplied return buffer.
*/
char *jwt_buf;
/** Size of the user provided buffer. */
size_t jwt_sz;
};

/**
* @brief Generates a JWT using the supplied parameters. If successful,
* the JWT string will be stored in the supplied struct.
* The user is responsible for providing a valid pointer to store the JWT.
*
* Subject and audience fields may be NULL in which case those fields are left out
* from generated JWT token.
*
* The API doesn't verify the time source validity, it is up to the caller to make sure
* that the system has access to a valid time source, otherwise "iat" field will
* contain the time since boot in seconds.
*
* JWT is signed with the application identity attestation key, no matter what
* value is supplied in the sec_tag.
*
* @param[in,out] jwt Pointer to struct containing JWT parameters and result.
*
* @retval 0 If the operation was successful.
* Otherwise, a (negative) error code is returned, check header errno.h for a full list.
*/
int app_jwt_generate(struct app_jwt_data *const jwt);

/**
* @brief Gets the device UUID from the secure domain
* and returns it as a NULL terminated string in the supplied buffer.
* The device UUID can be used as a device identifier for cloud services and
* for secure device management using the nRF Cloud Identity Service.
*
* UUID v4 defined by ITU-T X.667 | ISO/IEC 9834-8 has a length of 35 bytes, add
* 1 byte for the atring termination character. User is expected to provide a buffer
* of at least 36 bytes.
*
* @param[out] uuid_buffer Pointer to buffer where the device UUID string will be written to.
* @param[in] uuid_buffer_size Size of the provided buffer.
*
* @retval 0 If the operation was successful.
* Otherwise, a (negative) error code is returned, check header errno.h for a full list.
*/
int app_jwt_get_uuid(char *uuid_buffer, const size_t uuid_buffer_size);

/** @} */

#ifdef __cplusplus
}
#endif

#endif /* _APP_JWT_H */
1 change: 1 addition & 0 deletions lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ add_subdirectory_ifdef(CONFIG_HW_ID_LIBRARY hw_id)
add_subdirectory_ifdef(CONFIG_EDGE_IMPULSE edge_impulse)
add_subdirectory_ifdef(CONFIG_WAVE_GEN_LIB wave_gen)
add_subdirectory_ifdef(CONFIG_HW_UNIQUE_KEY_SRC hw_unique_key)
add_subdirectory_ifdef(CONFIG_APP_JWT app_jwt)
add_subdirectory_ifdef(CONFIG_MODEM_JWT modem_jwt)
add_subdirectory_ifdef(CONFIG_MODEM_SLM modem_slm)
add_subdirectory_ifdef(CONFIG_MODEM_ATTEST_TOKEN modem_attest_token)
Expand Down
1 change: 1 addition & 0 deletions lib/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

menu "Libraries"

rsource "app_jwt/Kconfig"
rsource "bin/Kconfig"
rsource "nrf_modem_lib/Kconfig"
rsource "adp536x/Kconfig"
Expand Down
11 changes: 11 additions & 0 deletions lib/app_jwt/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

zephyr_library()

zephyr_library_sources(
app_jwt.c
)
30 changes: 30 additions & 0 deletions lib/app_jwt/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
#

menuconfig APP_JWT
bool "Application JWT Library"
depends on SSF_CLIENT && SSF_PSA_CRYPTO_SERVICE_ENABLED && SSF_DEVICE_INFO_SERVICE_ENABLED
select BASE64
# Needed for time and date
select POSIX_API
# Needed to print integer values in JSON
select CJSON_LIB
select CBPRINTF_FP_SUPPORT

if APP_JWT

config APP_JWT_VERIFY_SIGNATURE
bool "Verify signature after signing"
default y

config APP_JWT_PRINT_EXPORTED_PUBKEY_DER
bool "Print to terminal the DER formatted public key"

module=APP_JWT
module-str=User App JWT
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"

endif # APP_JWT
Loading

0 comments on commit 9d95b0b

Please sign in to comment.