Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: app_jwt: add an application core jwt generator and add a sample for usage #19561

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# CODEOWNERS for autoreview assigning in github

Check failure on line 1 in CODEOWNERS

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

CODEOWNERS

Path '/doc/nrf/libraries/app_jwt/' not found in the treebut is listed in CODEOWNERS

Check failure on line 1 in CODEOWNERS

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

CODEOWNERS

New files added that are not covered in CODEOWNERS:

# https://help.github.com/en/articles/about-code-owners#codeowners-syntax
# Order is important; the last matching pattern takes the most
Expand Down 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 @@ -520,6 +523,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 @@ -641,6 +645,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
84 changes: 84 additions & 0 deletions doc/nrf/libraries/others/app_jwt.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
.. _lib_app_jwt:

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

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

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`

#. Generate a Signing key pair if you don't want to use the IAK Key.
#. 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::
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), "iat" field will contain the value :kconfig:option:`CONFIG_APP_JWT_DEFAULT_TIMESTAMP`.

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 ``kid`` claim.
Use ``false`` if you want to leave out this field.
If filled with the value ``true``, the claim ``kid`` will contain the SHA256 of the DER of the public part of the signing key.
* :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 ``false`` if you want to leave out this field.
If filled with the value ``true``, the claim ``iat`` will be filled with the current timestamp in seconds.
* :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
154 changes: 154 additions & 0 deletions include/app_jwt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* 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 an arbitrary timestamp.
*
* @param[in,out] jwt Pointer to struct containing JWT parameters and result.
*
* @retval 0 If the operation was successful.
* @retval -errno Negative errno for other failures.
*
*/
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.
* @retval -errno Negative errno for other failures.
*
*/
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
)
34 changes: 34 additions & 0 deletions lib/app_jwt/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#
# 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_DEFAULT_TIMESTAMP
int "Default timestamp to use in case time value is 0"
default 1735682400

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
Loading