Skip to content

Commit

Permalink
Merge pull request #599 from LedgerHQ/ledger-pki-sdk
Browse files Browse the repository at this point in the history
Ledger PKI feature
  • Loading branch information
srasoamiaramanana-ledger authored Jun 20, 2024
2 parents b031895 + 9df9fed commit be09f0e
Show file tree
Hide file tree
Showing 7 changed files with 431 additions and 12 deletions.
2 changes: 2 additions & 0 deletions Makefile.defines
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ DEFINES += HAVE_INAPP_BLE_PAIRING
DEFINES += HAVE_BATTERY
endif

DEFINES += HAVE_LEDGER_PKI

# include builtin CX libs options
-include $(BOLOS_SDK)/Makefile.conf.cx

Expand Down
6 changes: 6 additions & 0 deletions include/os_io_seproxyhal.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ unsigned short io_exchange_al(unsigned char channel_and_flags, unsigned short tx
// and version
unsigned int os_io_seproxyhal_get_app_name_and_version(void);

#if defined(HAVE_LEDGER_PKI)
unsigned int os_io_seproxyhal_pki_load_certificate(uint8_t *buffer,
size_t buffer_len,
uint8_t init);
#endif // HAVE_LEDGER_PKI

// for delegation of Native NFC / USB
unsigned char io_event(unsigned char channel);

Expand Down
227 changes: 227 additions & 0 deletions include/os_pki.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
#pragma once
#if defined(HAVE_LEDGER_PKI)
#include "decorators.h"
#include "errors.h"
#include "lcx_ecfp.h"
#include "ox_ec.h"
#include <stdint.h>
#include <stddef.h>

/** Certificate field with a variable length */
#define CERTIFICATE_FIELD_VAR_LEN (0xFF)
/** Certificate field with a non predefined value */
#define CERTIFICATE_FIELD_UNKNOWN_VALUE (0xFFFF)
/** Certificate validity index minimum value */
#define CERTIFICATE_VALIDITY_INDEX (0x0001)
/** Certificate structure type */
#define CERTIFICATE_STRUCTURE_TYPE_CERTIFICATE (0x01)
/** Maximum certificate trusted name length */
#define CERTIFICATE_TRUSTED_NAME_MAXLEN (32)

/** Certificate tags associated to each certificate field */
typedef enum {
CERTIFICATE_TAG_STRUCTURE_TYPE = 0x01, ///< Structure type
CERTIFICATE_TAG_VERSION = 0x02, ///< Certificate version
CERTIFICATE_TAG_VALIDITY = 0x10, ///< Certificate validity
CERTIFICATE_TAG_VALIDITY_INDEX = 0x11, ///< Certificate validity index
CERTIFICATE_TAG_CHALLENGE = 0x12, ///< Challenge value
CERTIFICATE_TAG_SIGNER_KEY_ID = 0x13, ///< Signer key ID
CERTIFICATE_TAG_SIGN_ALGO_ID = 0x14, ///< Signature algorithm with the signer key
CERTIFICATE_TAG_SIGNATURE = 0x15, ///< Signature
CERTIFICATE_TAG_TIME_VALIDITY = 0x16, ///< Time validity
CERTIFICATE_TAG_TRUSTED_NAME = 0x20, ///< Trusted name
CERTIFICATE_TAG_PUBLIC_KEY_ID = 0x30, ///< Public key ID
CERTIFICATE_TAG_PUBLIC_KEY_USAGE = 0x31, ///< Public key usage
CERTIFICATE_TAG_PUBLIC_KEY_CURVE_ID = 0x32, ///< Curve ID on which the public key is defined
CERTIFICATE_TAG_COMPRESSED_PUBLIC_KEY = 0x33, ///< Public key in compressed form
CERTIFICATE_TAG_PK_SIGN_ALGO_ID = 0x34, ///< Signature algorithm with the public key
CERTIFICATE_TAG_TARGET_DEVICE = 0x35, ///< Target device
CERTIFICATE_TAG_DEPTH = 0x36 ///< Certificate depth
} os_pki_tag_t;

/** Certificate version possible values */
enum {
CERTIFICATE_VERSION_02 = 0x02, ///< Certificate version 2
CERTIFICATE_VERSION_UNKNOWN
};

/** Certificate key ID possible values */
enum {
CERTIFICATE_KEY_ID_TEST = 0x0000,
CERTIFICATE_KEY_ID_PERSOV2,
CERTIFICATE_KEY_ID_LEDGER_ROOT_V3,
CERTIFICATE_KEY_ID_PLUGIN_SELECTOR,
CERTIFICATE_KEY_ID_NFT_METADATA,
CERTIFICATE_KEY_ID_PARTNER_METADATA,
CERTIFICATE_KEY_ID_ERC20_METADATA,
CERTIFICATE_KEY_ID_DOMAIN_METADATA,
CERTIFICATE_KEY_ID_UNKNOWN
};

/** Signature algorithm possible values */
enum {
CERTIFICATE_SIGN_ALGO_ID_ECDSA_SHA256 = 0x01,
CERTIFICATE_SIGN_ALGO_ID_ECDSA_SHA3_256 = 0x02,
CERTIFICATE_SIGN_ALGO_ID_ECDSA_KECCAK_256 = 0x03,
CERTIFICATE_SIGN_ALGO_ID_ECDSA_RIPEMD160 = 0x04,
CERTIFICATE_SIGN_ALGO_ID_EDDSA_SHA512 = 0x10,
CERTIFICATE_SIGN_ALGO_ID_UNKNOWN
};

/** Public key usages possible values */
enum {
CERTIFICATE_PUBLIC_KEY_USAGE_GENUINE_CHECK = 0x01,
CERTIFICATE_PUBLIC_KEY_USAGE_EXCHANGE_PAYLOAD,
CERTIFICATE_PUBLIC_KEY_USAGE_NFT_METADATA,
CERTIFICATE_PUBLIC_KEY_USAGE_TRUSTED_NAME,
CERTIFICATE_PUBLIC_KEY_USAGE_BACKUP_PROVIDER,
CERTIFICATE_PUBLIC_KEY_USAGE_RECOVER_ORCHESTRATOR,
CERTIFICATE_PUBLIC_KEY_USAGE_PLUGIN_METADATA,
CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META,
CERTIFICATE_PUBLIC_KEY_USAGE_SEED_ID_AUTH,
CERTIFICATE_PUBLIC_KEY_USAGE_UNKNOWN,
};

/** Target device possible values */
enum {
CERTIFICATE_TARGET_DEVICE_NANOS = 0x01,
CERTIFICATE_TARGET_DEVICE_NANOX,
CERTIFICATE_TARGET_DEVICE_NANOSP,
CERTIFICATE_TARGET_DEVICE_STAX,
CERTIFICATE_TARGET_DEVICE_UNKNOWN
};

/** Structure to store field length and field maximum value */
typedef struct {
uint16_t value;
uint8_t field_len;
} os_pki_certificate_tag_info_t;

/** Indices for #C_os_pki_certificate_tag_info */
enum {
CERTIFICATE_INFO_INDEX_STRUCTURE_TYPE = 0,
CERTIFICATE_INFO_INDEX_VERSION,
CERTIFICATE_INFO_INDEX_VALIDITY,
CERTIFICATE_INFO_INDEX_VALIDITY_INDEX,
CERTIFICATE_INFO_INDEX_CHALLENGE,
CERTIFICATE_INFO_INDEX_SIGNER_KEY_ID,
CERTIFICATE_INFO_INDEX_SIGN_ALGO_ID,
CERTIFICATE_INFO_INDEX_TIME_VALIDITY,
CERTIFICATE_INFO_INDEX_TRUSTED_NAME,
CERTIFICATE_INFO_INDEX_PUBLIC_KEY_ID,
CERTIFICATE_INFO_INDEX_PUBLIC_KEY_USAGE,
CERTIFICATE_INFO_INDEX_PUBLIC_KEY_CURVE_ID,
CERTIFICATE_INFO_INDEX_COMPRESSED_PUBLIC_KEY,
CERTIFICATE_INFO_INDEX_PK_SIGN_ALGO_ID,
CERTIFICATE_INFO_INDEX_TARGET_DEVICE,
CERTIFICATE_INFO_INDEX_SIGNATURE,
CERTIFICATE_INFO_INDEX_DEPTH
};

// clang-format off
/** Array of field length and field maximum value corresponding to each tag */
static const os_pki_certificate_tag_info_t C_os_pki_certificate_tag_info[] = {
[CERTIFICATE_INFO_INDEX_STRUCTURE_TYPE] = {CERTIFICATE_STRUCTURE_TYPE_CERTIFICATE, 0x01 },
[CERTIFICATE_INFO_INDEX_VERSION] = {CERTIFICATE_VERSION_UNKNOWN, 0x01 },
[CERTIFICATE_INFO_INDEX_VALIDITY] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, 0x04 },
[CERTIFICATE_INFO_INDEX_VALIDITY_INDEX] = {CERTIFICATE_VALIDITY_INDEX, 0x04 },
[CERTIFICATE_INFO_INDEX_CHALLENGE] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, CERTIFICATE_FIELD_VAR_LEN},
[CERTIFICATE_INFO_INDEX_SIGNER_KEY_ID] = {CERTIFICATE_KEY_ID_UNKNOWN, 0x02 },
[CERTIFICATE_INFO_INDEX_SIGN_ALGO_ID] = {CERTIFICATE_SIGN_ALGO_ID_ECDSA_RIPEMD160, 0x01 },
[CERTIFICATE_INFO_INDEX_TIME_VALIDITY] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, 0x04 },
[CERTIFICATE_INFO_INDEX_TRUSTED_NAME] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, CERTIFICATE_FIELD_VAR_LEN},
[CERTIFICATE_INFO_INDEX_PUBLIC_KEY_ID] = {CERTIFICATE_KEY_ID_UNKNOWN, 0x02 },
[CERTIFICATE_INFO_INDEX_PUBLIC_KEY_USAGE] = {CERTIFICATE_PUBLIC_KEY_USAGE_UNKNOWN, 0x01 },
[CERTIFICATE_INFO_INDEX_PUBLIC_KEY_CURVE_ID] = {CX_CURVE_TWISTED_EDWARDS_END, 0x01 },
[CERTIFICATE_INFO_INDEX_COMPRESSED_PUBLIC_KEY] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, CERTIFICATE_FIELD_VAR_LEN},
[CERTIFICATE_INFO_INDEX_PK_SIGN_ALGO_ID] = {CERTIFICATE_SIGN_ALGO_ID_UNKNOWN, 0x01 },
[CERTIFICATE_INFO_INDEX_TARGET_DEVICE] = {CERTIFICATE_TARGET_DEVICE_UNKNOWN, 0x01 },
[CERTIFICATE_INFO_INDEX_SIGNATURE] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, CERTIFICATE_FIELD_VAR_LEN},
[CERTIFICATE_INFO_INDEX_DEPTH] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, 0x01 },
};

static const cx_md_t C_os_sign_algo_hash_info[] = {
[CERTIFICATE_SIGN_ALGO_ID_ECDSA_SHA256] = CX_SHA256,
[CERTIFICATE_SIGN_ALGO_ID_ECDSA_SHA3_256] = CX_SHA3_256,
[CERTIFICATE_SIGN_ALGO_ID_ECDSA_KECCAK_256] = CX_KECCAK,
[CERTIFICATE_SIGN_ALGO_ID_ECDSA_RIPEMD160] = CX_RIPEMD160
};
// clang-format on

/**
* @brief Load a certificate and initialize the public key on success.
*
* @param[in] expected_key_usage Key verification role.
* @param[in] certificate_len Certificate length.
* @param[in] certificate_len Certificate
* @param[out] trusted_name Trusted name from the certificate
* @param[out] trusted_name_len Trusted name length
* @param[out] public_key Initialized public key from the certificate
*
* @return Error code
* @retval 0x0000 Success
* @retval 0x422F Incorrect structure type
* @retval 0x4230 Incorrect certificate version
* @retval 0x4231 Incorrect certificate validity
* @retval 0x4232 Incorrect certificate validity index
* @retval 0x4233 Unknown signer key ID
* @retval 0x4234 Unknown signature algorithm
* @retval 0x4235 Unknown public key ID
* @retval 0x4236 Unknown public key usage
* @retval 0x4237 Incorrect elliptic curve ID
* @retval 0x4238 Incorrect signature algorithm associated to the public key
* @retval 0x4239 Unknown target device
* @retval 0x422D Unknown certificate tag
* @retval 0x3301 Failed to hash data
* @retval 0x422E expected_key_usage doesn't match certificate key usage
* @retval 0x5720 Failed to verify signature
* @retval 0x4118 trusted_name buffer is too small to contain the trusted name
* @retval 0xFFFFFFxx Cryptography-related error
*/
SYSCALL bolos_err_t os_pki_load_certificate(uint8_t expected_key_usage,
uint8_t *certificate PLENGTH(certificate_len),
size_t certificate_len,
uint8_t *trusted_name,
size_t *trusted_name_len,
cx_ecfp_384_public_key_t *public_key);

/**
* @brief Verify a descriptor signature with internal public key.
*
* @details The 'load certificate' command must be sent before this function
* to initialize the internal public key.
* The caller is responsible for computing the descriptor hash prior
* to the verification.
*
* @param[in] descriptor_hash Hash of a descriptor
* @param[in] descriptor_hash_len Length of the descriptor hash
* @param[in] signature Signature over the descriptor
* @param[in] signature_len Signature length
* @return bool
* @retval true Success
* @retval false Failed to verify
*/
SYSCALL bool os_pki_verify(uint8_t *descriptor_hash PLENGTH(descriptor_hash_len),
size_t descriptor_hash_len,
uint8_t *signature PLENGTH(signature_len),
size_t signature_len);

/**
* @brief Get information from the last validated certificate.
*
* @param[out] key_usage Certificate role (expected key usage)
* @param[out] trusted_name Buffer for the trusted name.
* The size of the buffer must be less than
* #CERTIFICATE_TRUSTED_NAME_MAXLEN
* @param[out] trusted_name_len Trusted name length
* @param[out] public_key Certificate public key
* @return Error code
* @retval 0x0000 Success
* @retval 0x4119 trusted_name buffer is too small to contain the trusted name
* @retval 0x3202 Failed to initialize public key
*/
SYSCALL bolos_err_t os_pki_get_info(uint8_t *key_usage,
uint8_t *trusted_name,
size_t *trusted_name_len,
cx_ecfp_384_public_key_t *public_key);
#endif // HAVE_LEDGER_PKI
113 changes: 113 additions & 0 deletions include/sdk_apdu_commands.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#ifndef SDK_APDU_COMMANDS_H
#define SDK_APDU_COMMANDS_H

#if !defined(HAVE_BOLOS_NO_DEFAULT_APDU)
/** Instruction class */
#define DEFAULT_APDU_CLA 0xB0

/**
* @brief Instruction code with CLA = 0xB0 to get the version.
* @details If the OS is running, it returns the name "BOLOS"
* and the OS version. If an application is running,
* it returns the application name and its version.
*
* - Command APDU
* |FIELD |LENGTH |VALUE |DESCRIPTION |
* |------|-------|------|------------------|
* |CLA |0x01 |0xB0 |Instruction class |
* |INS |0x01 |0x01 |Instruction code |
* |P1 |0x01 |0x00 |None |
* |P2 |0x01 |0x00 |None |
* |LC |0x01 |0x00 |No data |
*
* - Response APDU
* |DATA |LENGTH |DESCRIPTION |
* |------------|------------|--------------------------------------|
* |NAME_LEN |0x01 |Length of the running process name |
* |NAME |NAME_LEN |Running process (OS or app) |
* |VERSION_LEN |0x01 |Version length of the running process |
* |VERSION |VERSION_LEN |Version of the running process |
* |STATUS_WORD |0x02 |0x9000 on success |
*/
#define DEFAULT_APDU_INS_GET_VERSION 0x01

#if defined(HAVE_SEED_COOKIE)
/**
* @brief Instruction code with CLA = 0xB0 to get a hash of
* a public key derived from the seed.
* @details The hash is computed by applying SHA512
* on the public key derived from the seed
* through a specific path.
*
* - Command APDU
* |FIELD |LENGTH |VALUE |DESCRIPTION |
* |------|-------|------|------------------|
* |CLA |0x01 |0xB0 |Instruction class |
* |INS |0x01 |0x02 |Instruction code |
* |P1 |0x01 |0x00 |None |
* |P2 |0x01 |0x00 |None |
* |LC |0x01 |0x00 |No data |
*
* - Response APDU
* |DATA |LENGTH | DESCRIPTION |
* |------------|-------|--------------------------------|
* |PK_HASH |0x200 | Hash of the derived public key |
* |STATUS_WORD |0x02 | 0x9000 on success |
*/
#define DEFAULT_APDU_INS_GET_SEED_COOKIE 0x02
#endif

#if defined(DEBUG_OS_STACK_CONSUMPTION)
#define DEFAULT_APDU_INS_STACK_CONSUMPTION 0x57
#endif // DEBUG_OS_STACK_CONSUMPTION

/**
* @brief Instruction code with CLA = 0xB0 to exit
* the running application.
*
* - Command APDU
* |FIELD |LENGTH |VALUE |DESCRIPTION |
* |------|-------|------|------------------|
* |CLA |0x01 |0xB0 |Instruction class |
* |INS |0x01 |0xA7 |Instruction code |
* |P1 |0x01 |0x00 |None |
* |P2 |0x01 |0x00 |None |
* |LC |0x01 |0x00 |No data |
*
* - Response APDU
* |DATA |LENGTH | DESCRIPTION |
* |------------|-------|-------------------|
* |STATUS_WORD |0x02 | 0x9000 on success |
*/
#define DEFAULT_APDU_INS_APP_EXIT 0xA7

#if defined(HAVE_LEDGER_PKI)
/**
* @brief Instruction code with CLA = 0xB0 to load a certificate.
* @details
* - Command APDU
* |FIELD |LENGTH |VALUE |DESCRIPTION |
* |------|--------|----------|-------------------|
* |CLA |0x01 |0xB0 |Instruction class |
* |INS |0x01 |0x06 |Instruction code |
* |P1 |0x01 |KEY_USAGE |Key usage |
* |P2 |0x01 |0x00 |None |
* |LC |0x01 |DATA_LEN |DATA length |
* |DATA |0x01 |CERT_LEN |Certificate length |
* |DATA |CERT_LEN|CERT |Certificate |
*
* - Response APDU
* |DATA |LENGTH | DESCRIPTION |
* |-----------------|-----------------|---------------------------------|
* |TRUSTED_NAME_LEN |0x01 | Certificate trusted name length |
* |TRUSTED_NAME |TRUSTED_NAME_LEN | Certificate trusted name |
* |PUBLIC_KEY_LEN |0x01 | Certificate public key length |
* |PUBLIC_KEY |PUBLIC_KEY_LEN | Certificate public key |
* |STATUS_WORD |0x02 | 0x9000 on success |
*/
#define DEFAULT_APDU_INS_LOAD_CERTIFICATE 0x06
#endif // HAVE_LEDGER_PKI

#endif // !HAVE_BOLOS_NO_DEFAULT_APDU

#endif /* SDK_APDU_COMMANDS_H */
5 changes: 5 additions & 0 deletions include/syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@
#define SYSCALL_os_allow_protected_ram_ID 0x00000092
#define SYSCALL_os_deny_protected_ram_ID 0x00000093
#define SYSCALL_os_set_ux_time_ms_ID 0x010000a2
#if defined(HAVE_LEDGER_PKI)
#define SYSCALL_os_pki_load_certificate_ID 0x060000aa
#define SYSCALL_os_pki_verify_ID 0x040000ab
#define SYSCALL_os_pki_get_info_ID 0x040000ac
#endif // HAVE_LEDGER_PKI

#ifdef HAVE_CUSTOM_CA_DETAILS_IN_SETTINGS
#define SYSCALL_os_bolos_custom_ca_get_info_ID 0x01000CA0
Expand Down
Loading

0 comments on commit be09f0e

Please sign in to comment.