From: David Bauer Date: Wed, 3 Jan 2024 14:16:19 +0000 (+0200) Subject: uim: add support for ICC communication channel X-Git-Url: http://git.openwrt.org/?a=commitdiff_plain;ds=sidebyside;p=project%2Fuqmi.git uim: add support for ICC communication channel This creates an interface to obtain a communication channel with the ICC of the modem. This interface can be used by other applications to interact with the SIM card, for example LPAd applications for managing connected eUICC. Signed-off-by: David Bauer --- diff --git a/uqmi/commands-uim.c b/uqmi/commands-uim.c index 924a959..d4bf3f3 100644 --- a/uqmi/commands-uim.c +++ b/uqmi/commands-uim.c @@ -19,7 +19,12 @@ * Boston, MA 02110-1301 USA. */ +#include "qmi-message.h" + static int uim_slot = 0; +static int channel_id = -1; +static uint8_t aid[16]; +static uint8_t apdu[1024]; #define cmd_uim_verify_pin1_cb no_cb static enum qmi_cmd_result @@ -183,3 +188,136 @@ cmd_uim_power_on_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qm qmi_set_uim_power_on_sim_request(msg, &data); return QMI_CMD_REQUEST; } + +#define cmd_uim_channel_id_cb no_cb +static enum qmi_cmd_result +cmd_uim_channel_id_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg) +{ + char *err; + int value = strtoul(arg, &err, 10); + if ((err && *err) || value < 1 || value > 4) { + uqmi_add_error("Invalid Channel-ID value. Allowed: [1,2,3,4]"); + return QMI_CMD_EXIT; + } + + channel_id = value; + + return QMI_CMD_DONE; +} + +static void cmd_uim_open_logical_channel_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg) +{ + struct qmi_uim_open_logical_channel_response res; + void *c; + + qmi_parse_uim_open_logical_channel_response(msg, &res); + + c = blobmsg_open_table(&status, NULL); + blobmsg_add_u32(&status, "channel_id", res.data.channel_id); + blobmsg_add_u32(&status, "sw1", res.data.card_result.sw1); + blobmsg_add_u32(&status, "sw2", res.data.card_result.sw2); + blobmsg_close_table(&status, c); +} + +static enum qmi_cmd_result +cmd_uim_open_logical_channel_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg) +{ + struct qmi_uim_open_logical_channel_request data = { + QMI_INIT(slot, uim_slot), + QMI_INIT_ARRAY(aid, aid, (strlen(arg) / 2)), + }; + + + if (!uim_slot) { + uqmi_add_error("UIM-Slot not set"); + return QMI_CMD_EXIT; + } + + if (!arg) { + uqmi_add_error("Missing AID argument"); + return QMI_CMD_EXIT; + } + + if (strlen(arg) % 2 || strlen(arg) > sizeof(aid) * 2 || + !uqmi_hexstring_parse(aid, (uint8_t *)arg, strlen(arg))) { + uqmi_add_error("Invalid AID argument"); + return QMI_CMD_EXIT; + } + + qmi_set_uim_open_logical_channel_request(msg, &data); + return QMI_CMD_REQUEST; +} + +#define cmd_uim_close_logical_channel_cb no_cb +static enum qmi_cmd_result +cmd_uim_close_logical_channel_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg) +{ + struct qmi_uim_logical_channel_request data = { + QMI_INIT(slot, uim_slot), + QMI_INIT(channel_id, channel_id), + }; + + if (!uim_slot) { + uqmi_add_error("UIM-Slot not set. Use --uim-slot to set it."); + return QMI_CMD_EXIT; + } + + if (channel_id < 1) { + uqmi_add_error("Invalid channel-id set."); + return QMI_CMD_EXIT; + } + + qmi_set_uim_logical_channel_request(msg, &data); + return QMI_CMD_REQUEST; +} + +static void cmd_uim_send_apdu_cb(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg) +{ + struct qmi_uim_send_apdu_response res; + uint8_t *hexstr; + void *c; + + qmi_parse_uim_send_apdu_response(msg, &res); + + hexstr = calloc(1, res.data.apdu_response_n * 2 + 1); + if (!hexstr) + return; + + uqmi_hexstring_create(hexstr, res.data.apdu_response, res.data.apdu_response_n); + + c = blobmsg_open_table(&status, NULL); + blobmsg_add_string(&status, "response", (char *)hexstr); + blobmsg_close_table(&status, c); + + free(hexstr); +} + +static enum qmi_cmd_result +cmd_uim_send_apdu_prepare(struct qmi_dev *qmi, struct qmi_request *req, struct qmi_msg *msg, char *arg) +{ + struct qmi_uim_send_apdu_request data = { + QMI_INIT(slot, uim_slot), + QMI_INIT(channel_id, channel_id), + QMI_INIT_ARRAY(apdu, apdu, (strlen(arg) / 2)), + }; + + + if (!uim_slot) { + uqmi_add_error("UIM-Slot not set. Use --uim-slot to set it."); + return QMI_CMD_EXIT; + } + + if (!arg) { + uqmi_add_error("Missing APDU argument"); + return QMI_CMD_EXIT; + } + + if (strlen(arg) % 2 || strlen(arg) > sizeof(apdu) * 2 || + !uqmi_hexstring_parse(apdu, (uint8_t *)arg, strlen(arg))) { + uqmi_add_error("Invalid APDU argument"); + return QMI_CMD_EXIT; + } + + qmi_set_uim_send_apdu_request(msg, &data); + return QMI_CMD_REQUEST; +} diff --git a/uqmi/commands-uim.h b/uqmi/commands-uim.h index f35c09f..3c0a3e7 100644 --- a/uqmi/commands-uim.h +++ b/uqmi/commands-uim.h @@ -25,7 +25,11 @@ __uqmi_command(uim_verify_pin2, uim-verify-pin2, required, QMI_SERVICE_UIM), \ __uqmi_command(uim_get_sim_state, uim-get-sim-state, no, QMI_SERVICE_UIM), \ __uqmi_command(uim_power_off, uim-power-off, no, QMI_SERVICE_UIM), \ - __uqmi_command(uim_power_on, uim-power-on, no, QMI_SERVICE_UIM) \ + __uqmi_command(uim_power_on, uim-power-on, no, QMI_SERVICE_UIM), \ + __uqmi_command(uim_channel_id, uim-channel-id, required, CMD_TYPE_OPTION), \ + __uqmi_command(uim_open_logical_channel, uim-channel-open, required, QMI_SERVICE_UIM), \ + __uqmi_command(uim_close_logical_channel, uim-channel-close, no, QMI_SERVICE_UIM), \ + __uqmi_command(uim_send_apdu, uim-apdu-send, required, QMI_SERVICE_UIM) \ #define uim_helptext \ @@ -36,4 +40,12 @@ " --uim-slot: SIM slot [1-2]\n" \ " --uim-power-on: Power on SIM card\n" \ " --uim-slot: SIM slot [1-2]\n" \ + " --uim-channel-open : Open channel for AID\n" \ + " --uim-slot: SIM slot [1-2]\n" \ + " --uim-channel-close: Close channel\n" \ + " --uim-slot: SIM slot [1-2]\n" \ + " --uim-channel-id: Channel-id\n" \ + " --uim-apdu-send : Send APDU command to ICC\n" \ + " --uim-slot: SIM slot [1-2]\n" \ + " --uim-channel-id: Channel-id\n" \ diff --git a/uqmi/uqmi.h b/uqmi/uqmi.h index 19140fe..c6897f2 100644 --- a/uqmi/uqmi.h +++ b/uqmi/uqmi.h @@ -123,4 +123,54 @@ int qmi_service_get_client_id(struct qmi_dev *qmi, QmiService svc); int qmi_service_release_client_id(struct qmi_dev *qmi, QmiService svc); QmiService qmi_service_get_by_name(const char *str); +static inline uint8_t *uqmi_hexstring_parse(uint8_t *output, + const uint8_t *hexstr, + size_t hexstr_size) +{ + uint8_t *out = output; + size_t i; + + for (i = 0; i < hexstr_size; i += 2) { + if (hexstr[i] >= '0' && hexstr[i] <= '9') + *out = (hexstr[i] - '0') << 4; + else if (hexstr[i] >= 'a' && hexstr[i] <= 'f') + *out = (hexstr[i] - 'a' + 10) << 4; + else if (hexstr[i] >= 'A' && hexstr[i] <= 'F') + *out = (hexstr[i] - 'A' + 10) << 4; + else + return NULL; + + if (i + 1 >= hexstr_size) + return NULL; + + if (hexstr[i + 1] >= '0' && hexstr[i + 1] <= '9') + *out |= hexstr[i + 1] - '0'; + else if (hexstr[i + 1] >= 'a' && hexstr[i + 1] <= 'f') + *out |= hexstr[i + 1] - 'a' + 10; + else if (hexstr[i + 1] >= 'A' && hexstr[i + 1] <= 'F') + *out |= hexstr[i + 1] - 'A' + 10; + else + return NULL; + + out++; + } + + return output; +} + +static inline uint8_t *uqmi_hexstring_create(uint8_t *output, + const uint8_t *data, + size_t data_size) +{ + uint8_t *out = output; + size_t i; + + for (i = 0; i < data_size; i++) { + *out++ = "0123456789abcdef"[data[i] >> 4]; + *out++ = "0123456789abcdef"[data[i] & 0xf]; + } + + return output; +} + #endif