uim: add support for ICC communication channel master
authorDavid Bauer <david.bauer@uniberg.com>
Wed, 3 Jan 2024 14:16:19 +0000 (16:16 +0200)
committerDavid Bauer <mail@david-bauer.net>
Sun, 25 Aug 2024 00:36:21 +0000 (02:36 +0200)
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 <david.bauer@uniberg.com>
uqmi/commands-uim.c
uqmi/commands-uim.h
uqmi/uqmi.h

index 924a95965ef44f9640dcb8d43e52f99187b58d8c..d4bf3f304da0c67ccbe71994719aa416d80a528f 100644 (file)
  * 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 <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 <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;
+}
index f35c09f66b9bd4b57aa70e06098cf28a79f9ab90..3c0a3e752c00a38eec3fdd15b4d9a700b16be9e5 100644 (file)
        __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 \
                "    --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 <AID>:         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 <cmd>:            Send APDU command to ICC\n" \
+               "    --uim-slot:                     SIM slot [1-2]\n" \
+               "    --uim-channel-id:               Channel-id\n" \
 
index 19140fe43ceadbc4f8a05fed972180ba9ab83b1c..c6897f29e5f4882424b14d04d23e43faa5a8e6d9 100644 (file)
@@ -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