/* * Create an empty security environment */ static int incrypto34_create_sec_env(struct sc_profile *profile, struct sc_card *card, unsigned int se_id, unsigned int key_id) { struct sc_cardctl_incrypto34_obj_info args; struct tlv tlv; unsigned char buffer[64]; tlv_init(&tlv, buffer, sizeof(buffer)); tlv_next(&tlv, 0x83); tlv_add(&tlv, se_id); tlv_next(&tlv, 0x86); tlv_add(&tlv, 0); tlv_add(&tlv, 0); tlv_next(&tlv, 0x8f); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); args.data = buffer; args.len = tlv_len(&tlv); return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_SECI, &args); }
/* * Create an empty security environment */ static int cardos_create_sec_env(struct sc_profile *profile, sc_card_t *card, unsigned int se_id, unsigned int key_id) { struct sc_cardctl_cardos_obj_info args; struct tlv tlv; unsigned char buffer[64]; int r; tlv_init(&tlv, buffer, sizeof(buffer)); tlv_next(&tlv, 0x83); tlv_add(&tlv, se_id); tlv_next(&tlv, 0x86); tlv_add(&tlv, 0); tlv_add(&tlv, 0); tlv_next(&tlv, 0x8f); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); tlv_add(&tlv, key_id); args.data = buffer; args.len = tlv_len(&tlv); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) return r; return sc_card_ctl(card, SC_CARDCTL_CARDOS_PUT_DATA_SECI, &args); }
void * tlv_nest_start(struct tlv_buf *buf, int id, int len) { unsigned long offset = attr_to_offset(buf, buf->head); buf->head = tlv_add(buf, tlv_next(buf->head), id, len); return (void *) offset; }
/* * Full-length of all TLV fields. */ size_t tlv_sequence_length (uint8_t *stream) { size_t res = 0; do { res += tlv_record_length (stream, NULL, NULL); } while ((stream = tlv_next (stream))); return res; }
struct tlv_attr * tlv_new(struct tlv_buf *buf, int id, int payload) { struct tlv_attr *attr; attr = tlv_add(buf, tlv_next(buf->head), id, payload); if (!attr) return NULL; tlv_set_raw_len(buf->head, tlv_pad_len(buf->head) + tlv_pad_len(attr)); return attr; }
static struct tlv * qmi_tlv_next (QmiMessage *self, struct tlv *tlv) { struct tlv *end; struct tlv *next; end = (struct tlv *) qmi_end (self); next = tlv_next (tlv); return (next < end ? next : NULL); }
struct tlv_attr * tlv_put_raw(struct tlv_buf *buf, const void *ptr, int len) { struct tlv_attr *attr; if (len < (int)sizeof(struct tlv_attr) || !ptr) return NULL; attr = tlv_add(buf, tlv_next(buf->head), 0, len - sizeof(struct tlv_attr)); tlv_set_raw_len(buf->head, tlv_pad_len(buf->head) + len); memcpy(attr, ptr, len); return attr; }
static int cardos_store_key_component(sc_card_t *card, int algorithm, unsigned int key_id, unsigned int pin_id, unsigned int num, const u8 *data, size_t len, int last, int use_prefix) { struct sc_cardctl_cardos_obj_info args; struct tlv tlv; unsigned char buffer[256]; #ifdef SET_SM_BYTES unsigned int n; #endif int r; /* Initialize the TLV encoder */ tlv_init(&tlv, buffer, sizeof(buffer)); /* Object address */ tlv_next(&tlv, 0x83); tlv_add(&tlv, 0x20|num); /* PSO, n-th component */ tlv_add(&tlv, key_id); /* Object parameters */ tlv_next(&tlv, 0x85); tlv_add(&tlv, CARDOS_KEY_OPTIONS|(last? 0x00 : 0x20)); tlv_add(&tlv, CARDOS_KEY_FLAGS); tlv_add(&tlv, algorithm); tlv_add(&tlv, 0x00); tlv_add(&tlv, 0xFF); /* use count */ tlv_add(&tlv, 0xFF); /* DEK (whatever this is) */ tlv_add(&tlv, 0x00); tlv_add(&tlv, 0x00); /* AC bytes */ tlv_next(&tlv, 0x86); tlv_add(&tlv, pin_id); /* AC USE */ tlv_add(&tlv, pin_id); /* AC CHANGE */ tlv_add(&tlv, pin_id); /* UNKNOWN */ tlv_add(&tlv, 0); /* rfu */ tlv_add(&tlv, 0); /* rfu */ tlv_add(&tlv, 0); /* rfu */ #if 0 tlv_add(&tlv, pin_id); /* AC GENKEY */ #else tlv_add(&tlv, 0); #endif #ifdef SET_SM_BYTES /* it shouldn't be necessary to set the default value */ /* SM bytes */ tlv_next(&tlv, 0x8B); for (n = 0; n < 16; n++) tlv_add(&tlv, 0xFF); #endif /* key component */ tlv_next(&tlv, 0x8f); if (use_prefix != 0) { tlv_add(&tlv, len+1); tlv_add(&tlv, 0); } while (len--) tlv_add(&tlv, *data++); args.data = buffer; args.len = tlv_len(&tlv); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) return r; return sc_card_ctl(card, SC_CARDCTL_CARDOS_PUT_DATA_OCI, &args); }
/* * Store a PIN or PUK */ static int cardos_store_pin(sc_profile_t *profile, sc_card_t *card, sc_pkcs15_pin_info_t *pin_info, int puk_id, const u8 *pin, size_t pin_len) { struct sc_cardctl_cardos_obj_info args; unsigned char buffer[256]; unsigned char pinpadded[256]; struct tlv tlv; unsigned int attempts, minlen, maxlen; int r; /* We need to do padding because pkcs15-lib.c does it. * Would be nice to have a flag in the profile that says * "no padding required". */ maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded)); if (pin_len > maxlen) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "invalid pin length: %u (max %u)\n", pin_len, maxlen); return SC_ERROR_INVALID_ARGUMENTS; } memcpy(pinpadded, pin, pin_len); while (pin_len < maxlen) pinpadded[pin_len++] = profile->pin_pad_char; pin = pinpadded; attempts = pin_info->tries_left; minlen = pin_info->min_length; tlv_init(&tlv, buffer, sizeof(buffer)); /* object address: class, id */ tlv_next(&tlv, 0x83); tlv_add(&tlv, 0x00); /* class byte: usage TEST, k=0 */ tlv_add(&tlv, pin_info->reference); /* parameters */ tlv_next(&tlv, 0x85); tlv_add(&tlv, 0x02); /* options byte */ tlv_add(&tlv, attempts & 0xf); /* flags byte */ tlv_add(&tlv, CARDOS_ALGO_PIN); /* algorithm = pin-test */ tlv_add(&tlv, attempts & 0xf); /* errcount = attempts */ /* usecount: not documented, but seems to work like this: * - value of 0xff means pin can be presented any number * of times * - anything less: max # of times before BS object is blocked. */ tlv_add(&tlv, 0xff); /* DEK: not documented, no idea what it means */ tlv_add(&tlv, 0xff); /* ARA counter: number of times the test object can be used before * another verification is required (~ user consent) * (0x00 unlimited usage) */ tlv_add(&tlv, 0x00); tlv_add(&tlv, minlen); /* minlen */ /* AC conditions */ tlv_next(&tlv, 0x86); tlv_add(&tlv, 0x00); /* use: always */ tlv_add(&tlv, pin_info->reference); /* change: PIN */ tlv_add(&tlv, puk_id); /* unblock: PUK */ /* data: PIN */ tlv_next(&tlv, 0x8f); while (pin_len--) tlv_add(&tlv, *pin++); args.data = buffer; args.len = tlv_len(&tlv); /* ensure we are in the correct lifecycle */ r = sc_pkcs15init_set_lifecycle(card, SC_CARDCTRL_LIFECYCLE_ADMIN); if (r < 0 && r != SC_ERROR_NOT_SUPPORTED) return r; return sc_card_ctl(card, SC_CARDCTL_CARDOS_PUT_DATA_OCI, &args); }
static int incrypto34_store_key_component(struct sc_card *card, int algorithm, unsigned int key_id, unsigned int pin_id, unsigned int num, const u8 *data, size_t len, int last) { int r; struct sc_cardctl_incrypto34_obj_info args; struct tlv tlv; unsigned char buffer[256]; unsigned int n; /* Initialize the TLV encoder */ tlv_init(&tlv, buffer, sizeof(buffer)); /* Object address */ tlv_next(&tlv, 0x83); tlv_add(&tlv, 0x20|num); /* PSO, n-th component */ tlv_add(&tlv, key_id); /* Object parameters */ tlv_next(&tlv, 0x85); tlv_add(&tlv, INCRYPTO34_KEY_OPTIONS|(last? 0x00 : 0x20)); tlv_add(&tlv, INCRYPTO34_KEY_FLAGS); tlv_add(&tlv, algorithm); tlv_add(&tlv, 0x0F); /* Error Counter*/ tlv_add(&tlv, 0xFF); /* use count */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0x00); /* RFU */ tlv_add(&tlv, 0x00); /* RFU */ /* AC bytes */ tlv_next(&tlv, 0x86); tlv_add(&tlv, pin_id); /* AC USE */ tlv_add(&tlv, pin_id); /* AC CHANGE */ tlv_add(&tlv, 0xFF); /* AC_UNBLOCK */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0); /* AC_GENKEY */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0xFF); /* RFU */ tlv_add(&tlv, 0xFF); /* RFU */ /* SM bytes */ tlv_next(&tlv, 0x8B); for (n = 0; n < 16; n++) tlv_add(&tlv, 0xFF); /* key component */ tlv_next(&tlv, 0x8f); tlv_add(&tlv, len+1); tlv_add(&tlv, 0); while (len--) tlv_add(&tlv, *data++); args.data = buffer; args.len = tlv_len(&tlv); r = sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, &args); return r; }
/* * Store a PIN or PUK */ static int incrypto34_store_pin(sc_profile_t *profile, sc_card_t *card, sc_pkcs15_auth_info_t *auth_info, int puk_id, const u8 *pin, size_t pin_len) { struct sc_cardctl_incrypto34_obj_info args; unsigned char buffer[256]; unsigned char pinpadded[16]; struct tlv tlv; unsigned int attempts, minlen, maxlen; if (auth_info->auth_type != SC_PKCS15_PIN_AUTH_TYPE_PIN) return SC_ERROR_OBJECT_NOT_VALID; /* We need to do padding because pkcs15-lib.c does it. * Would be nice to have a flag in the profile that says * "no padding required". */ maxlen = MIN(profile->pin_maxlen, sizeof(pinpadded)); if (pin_len > maxlen) pin_len = maxlen; memcpy(pinpadded, pin, pin_len); while (pin_len < maxlen) pinpadded[pin_len++] = profile->pin_pad_char; pin = pinpadded; attempts = auth_info->tries_left; minlen = auth_info->attrs.pin.min_length; tlv_init(&tlv, buffer, sizeof(buffer)); /* object address: class, id */ tlv_next(&tlv, 0x83); tlv_add(&tlv, 0x00); /* class byte: usage TEST, k=0 */ tlv_add(&tlv, auth_info->attrs.pin.reference); /* parameters */ tlv_next(&tlv, 0x85); tlv_add(&tlv, 0x02); /* options byte */ tlv_add(&tlv, attempts & 0xf); /* flags byte */ tlv_add(&tlv, INCRYPTO34_ALGO_PIN); /* algorithm = pin-test */ tlv_add(&tlv, attempts & 0xf); /* errcount = attempts */ /* usecount: not documented, but seems to work like this: * - value of 0xff means pin can be presented any number * of times * - anything less: max # of times before BS object is blocked. */ tlv_add(&tlv, 0xff); /* DEK: RFU */ tlv_add(&tlv, 0x00); /* ARA counter: the number of times the PIN can be used before the he must be verified again (0 or ff for unlimited usage) */ tlv_add(&tlv, 0x00); tlv_add(&tlv, minlen); /* minlen */ /* AC conditions */ tlv_next(&tlv, 0x86); tlv_add(&tlv, 0x00); /* use: always */ tlv_add(&tlv, auth_info->attrs.pin.reference); /* change: PIN */ tlv_add(&tlv, puk_id); /* unblock: PUK */ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*unused on pins*/ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*RFU*/ tlv_add(&tlv, 0xFF); /*RFU*/ /* data: PIN */ tlv_next(&tlv, 0x8f); while (pin_len--) tlv_add(&tlv, *pin++); args.data = buffer; args.len = tlv_len(&tlv); return sc_card_ctl(card, SC_CARDCTL_INCRYPTO34_PUT_DATA_OCI, &args); }
/** * Checks the validity of a QMI message. * * In particular, checks: * 1. The message has space for all required headers. * 2. The length of the buffer, the qmux length field, and the QMI tlv_length * field are all consistent. * 3. The TLVs in the message fit exactly in the payload size. * * Returns non-zero if the message is valid, zero if invalid. */ gboolean qmi_message_check (QmiMessage *self, GError **error) { gsize header_length; gchar *end; struct tlv *tlv; g_assert (self != NULL); g_assert (self->buf != NULL); if (self->buf->marker != QMI_MESSAGE_QMUX_MARKER) { g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_MESSAGE, "Marker is incorrect"); return FALSE; } if (qmux_length (self) < sizeof (struct qmux)) { g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_MESSAGE, "QMUX length too short for QMUX header (%u < %" G_GSIZE_FORMAT ")", qmux_length (self), sizeof (struct qmux)); return FALSE; } /* * qmux length is one byte shorter than buffer length because qmux * length does not include the qmux frame marker. */ if (qmux_length (self) != self->len - 1) { g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_MESSAGE, "QMUX length and buffer length don't match (%u != %" G_GSIZE_FORMAT ")", qmux_length (self), self->len - 1); return FALSE; } header_length = sizeof (struct qmux) + (qmi_message_is_control (self) ? sizeof (struct control_header) : sizeof (struct service_header)); if (qmux_length (self) < header_length) { g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_MESSAGE, "QMUX length too short for QMI header (%u < %" G_GSIZE_FORMAT ")", qmux_length (self), header_length); return FALSE; } if (qmux_length (self) - header_length != qmi_tlv_length (self)) { g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_MESSAGE, "QMUX length and QMI TLV lengths don't match (%u - %" G_GSIZE_FORMAT " != %u)", qmux_length (self), header_length, qmi_tlv_length (self)); return FALSE; } end = qmi_end (self); for (tlv = qmi_tlv (self); tlv < (struct tlv *)end; tlv = tlv_next (tlv)) { if (tlv->value > end) { g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_MESSAGE, "TLV header runs over buffer (%p > %p)", tlv->value, end); return FALSE; } if (tlv->value + le16toh (tlv->length) > end) { g_set_error (error, QMI_CORE_ERROR, QMI_CORE_ERROR_INVALID_MESSAGE, "TLV value runs over buffer (%p + %u > %p)", tlv->value, le16toh (tlv->length), end); return FALSE; } } /* * If this assert triggers, one of the if statements in the loop is wrong. * (It shouldn't be reached on malformed QMI messages.) */ g_assert (tlv == (struct tlv *)end); return TRUE; }