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

WIP: Ledger PKI feature #599

Open
wants to merge 6 commits into
base: master
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
207 changes: 207 additions & 0 deletions include/os_pki.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
#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 (0xFFFFFFFF)
/** Certificate validity index minimum value */
#define CERTIFICATE_VALIDITY_INDEX (0x00000001)
/** 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 {
uint32_t value;
uint8_t field_len;
} os_pki_certificate_tag_info_t;

// 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_TAG_STRUCTURE_TYPE] = {CERTIFICATE_STRUCTURE_TYPE_CERTIFICATE, 0x01 },
[CERTIFICATE_TAG_VERSION] = {CERTIFICATE_VERSION_UNKNOWN, 0x01 },
[CERTIFICATE_TAG_VALIDITY] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, 0x04 },
[CERTIFICATE_TAG_VALIDITY_INDEX] = {CERTIFICATE_VALIDITY_INDEX, 0x04 },
[CERTIFICATE_TAG_CHALLENGE] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, CERTIFICATE_FIELD_VAR_LEN},
[CERTIFICATE_TAG_SIGNER_KEY_ID] = {CERTIFICATE_KEY_ID_UNKNOWN, 0x02 },
[CERTIFICATE_TAG_SIGN_ALGO_ID] = {CERTIFICATE_SIGN_ALGO_ID_UNKNOWN, 0x01 },
[CERTIFICATE_TAG_TIME_VALIDITY] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, 0x04 },
[CERTIFICATE_TAG_TRUSTED_NAME] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, CERTIFICATE_FIELD_VAR_LEN},
[CERTIFICATE_TAG_PUBLIC_KEY_ID] = {CERTIFICATE_KEY_ID_UNKNOWN, 0x02 },
[CERTIFICATE_TAG_PUBLIC_KEY_USAGE] = {CERTIFICATE_PUBLIC_KEY_USAGE_UNKNOWN, 0x01 },
[CERTIFICATE_TAG_PUBLIC_KEY_CURVE_ID] = {CX_CURVE_TWISTED_EDWARDS_END, 0x01 },
[CERTIFICATE_TAG_COMPRESSED_PUBLIC_KEY] = {CERTIFICATE_KEY_ID_UNKNOWN, CERTIFICATE_FIELD_VAR_LEN},
[CERTIFICATE_TAG_PK_SIGN_ALGO_ID] = {CERTIFICATE_SIGN_ALGO_ID_UNKNOWN, 0x01 },
[CERTIFICATE_TAG_TARGET_DEVICE] = {CERTIFICATE_TARGET_DEVICE_UNKNOWN, 0x01 },
[CERTIFICATE_TAG_SIGNATURE] = {CERTIFICATE_FIELD_UNKNOWN_VALUE, CERTIFICATE_FIELD_VAR_LEN},
[CERTIFICATE_TAG_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,
[CERTIFICATE_SIGN_ALGO_ID_EDDSA_SHA512] = CX_SHA512
};
// 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