Blob Blame History Raw
diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c
index 099923e5..61e69c88 100644
--- a/src/libopensc/card-cac.c
+++ b/src/libopensc/card-cac.c
@@ -1793,7 +1793,7 @@ static int cac_find_and_initialize(sc_card_t *card, int initialize)
 		}
 		r = cac_process_ACA(card, priv);
 		if (r == SC_SUCCESS) {
-			card->type = SC_CARD_TYPE_CAC_II;
+			card->type = SC_CARD_TYPE_CAC_ALT_HID;
 			card->drv_data = priv;
 			return r;
 		}
@@ -1869,6 +1869,8 @@ static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries
 	 * FIPS 201 4.1.6.1 (numeric only) and * FIPS 140-2
 	 * (6 character minimum) requirements.
 	 */
+	sc_apdu_t apdu;
+	u8  sbuf[SC_MAX_APDU_BUFFER_SIZE];
 	struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
 
 	if (data->cmd == SC_PIN_CMD_CHANGE) {
@@ -1881,6 +1883,18 @@ static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries
 				return SC_ERROR_INVALID_DATA;
 			}
 		}
+
+		/* We can change the PIN of Giesecke & Devrient CAC ALT tokens
+		 * with a bit non-standard APDU */
+		if (card->type == SC_CARD_TYPE_CAC_ALT_HID) {
+			int r = 0;
+			r = iso7816_build_pin_apdu(card, &apdu, data, sbuf, sizeof(sbuf));
+			if (r < 0)
+				return r;
+			/* it requires P1 = 0x01 completely against the ISO specs */
+			apdu.p1 = 0x01;
+			data->apdu = &apdu;
+		}
 	}
 
 	return  iso_drv->ops->pin_cmd(card, data, tries_left);
diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
index 0ec25a46..16846d15 100644
--- a/src/libopensc/cards.h
+++ b/src/libopensc/cards.h
@@ -244,6 +244,7 @@ enum {
 	SC_CARD_TYPE_CAC_GENERIC,
 	SC_CARD_TYPE_CAC_I,
 	SC_CARD_TYPE_CAC_II,
+	SC_CARD_TYPE_CAC_ALT_HID,
 
 	/* nPA cards */
 	SC_CARD_TYPE_NPA = 34000,
diff --git a/src/libopensc/iso7816.c b/src/libopensc/iso7816.c
index b1a0e88f..d41613b2 100644
--- a/src/libopensc/iso7816.c
+++ b/src/libopensc/iso7816.c
@@ -1017,7 +1017,7 @@ iso7816_decipher(struct sc_card *card,
 }
 
 
-static int
+int
 iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu,
 		struct sc_pin_cmd_data *data, u8 *buf, size_t buf_len)
 {
diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h
index b519c5d5..8ebf9fbd 100644
--- a/src/libopensc/opensc.h
+++ b/src/libopensc/opensc.h
@@ -1664,6 +1664,19 @@ int iso7816_update_binary_sfid(sc_card_t *card, unsigned char sfid,
  * */
 int iso7816_logout(sc_card_t *card, unsigned char pin_reference);
 
+/*
+ * @brief Format PIN APDU for modifiction by card driver
+ *
+ * @param[in] card           card
+ * @param[in] apdu           apdu structure to update with PIN APDU
+ * @param[in] data           pin command data to set into the APDU
+ * @param[in] buf            buffer for APDU data field
+ * @param[in] buf_len        maximum buffer length
+ */
+int
+iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu,
+		struct sc_pin_cmd_data *data, u8 *buf, size_t buf_len);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/libopensc/pkcs15-cac.c b/src/libopensc/pkcs15-cac.c
index ccb27994..05056ea9 100644
--- a/src/libopensc/pkcs15-cac.c
+++ b/src/libopensc/pkcs15-cac.c
@@ -79,6 +79,7 @@ static const char * cac_get_name(int type)
     switch (type) {
     case SC_CARD_TYPE_CAC_I: return ("CAC I");
     case SC_CARD_TYPE_CAC_II: return ("CAC II");
+    case SC_CARD_TYPE_CAC_ALT_HID: return ("CAC ALT HID");
     default: break;
     }
     return ("CAC");