From aadd82bb071e574fc57263a103e3bf06ebbd8de7 Mon Sep 17 00:00:00 2001
From: "Ingo Struck (git commits)" <gitlab@ingostruck.de>
Date: Sat, 21 Jan 2023 22:15:10 +0100
Subject: [PATCH] Handle reader limits for SC Card unwrap operations

Fixes #2514
---
 src/libopensc/card-sc-hsm.c |  181 ++++++++++++++++++++++++--------------------
 src/libopensc/reader-pcsc.c |   90 +++++++++++++--------
 2 files changed, 157 insertions(+), 114 deletions(-)

Index: opensc-0.19.0/src/libopensc/card-sc-hsm.c
===================================================================
--- opensc-0.19.0.orig/src/libopensc/card-sc-hsm.c
+++ opensc-0.19.0/src/libopensc/card-sc-hsm.c
@@ -143,9 +143,7 @@ static int sc_hsm_select_file_ex(sc_card
 
 	if (file_out == NULL) {				// Versions before 0.16 of the SmartCard-HSM do not support P2='0C'
 		rv = sc_hsm_select_file_ex(card, in_path, forceselect, &file);
-		if (file != NULL) {
-			sc_file_free(file);
-		}
+		sc_file_free(file);
 		return rv;
 	}
 
@@ -179,9 +177,7 @@ static int sc_hsm_select_file_ex(sc_card
 			LOG_TEST_RET(card->ctx, rv, "Could not select SmartCard-HSM application");
 
 			if (priv) {
-				if (priv->dffcp != NULL) {
-					sc_file_free(priv->dffcp);
-				}
+				sc_file_free(priv->dffcp);
 				// Cache the FCP returned when selecting the applet
 				sc_file_dup(&priv->dffcp, *file_out);
 			}
@@ -843,47 +839,61 @@ static int sc_hsm_write_ef(sc_card_t *ca
 		LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
 	}
 
-	p = cmdbuff;
-	*p++ = 0x54;
-	*p++ = 0x02;
-	*p++ = (idx >> 8) & 0xFF;
-	*p++ = idx & 0xFF;
-	*p++ = 0x53;
-	if (count < 128) {
-		*p++ = (u8) count;
-		len = 6;
-	} else if (count < 256) {
-		*p++ = 0x81;
-		*p++ = (u8) count;
-		len = 7;
-	} else {
-		*p++ = 0x82;
-		*p++ = (count >> 8) & 0xFF;
-		*p++ = count & 0xFF;
-		len = 8;
-	}
+	size_t bytes_left = count;
+	// 8 bytes are required for T54(4) and T53(4)
+	size_t blk_size = card->max_send_size - 8;
+	size_t to_send = 0;
+	size_t offset = (size_t) idx;
+	do {
+		len = 0;
+		to_send = bytes_left >= blk_size ? blk_size : bytes_left;
+		p = cmdbuff;
+		// ASN1 0x54 offset
+		*p++ = 0x54;
+		*p++ = 0x02;
+		*p++ = (offset >> 8) & 0xFF;
+		*p++ = offset & 0xFF;
+		// ASN1 0x53 to_send
+		*p++ = 0x53;
+		if (to_send < 128) {
+			*p++ = (u8)to_send;
+			len = 6;
+		} else if (to_send < 256) {
+			*p++ = 0x81;
+			*p++ = (u8)to_send;
+			len = 7;
+		} else {
+			*p++ = 0x82;
+			*p++ = (to_send >> 8) & 0xFF;
+			*p++ = to_send & 0xFF;
+			len = 8;
+		}
 
-	if (buf != NULL)
-		memcpy(p, buf, count);
-	len += count;
+		if (buf != NULL)
+			memcpy(p, buf+offset, to_send);
+		len += to_send;
+
+		sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD7, fid >> 8, fid & 0xFF);
+		apdu.data = cmdbuff;
+		apdu.datalen = len;
+		apdu.lc = len;
 
-	sc_format_apdu(card, &apdu, SC_APDU_CASE_3, 0xD7, fid >> 8, fid & 0xFF);
-	apdu.data = cmdbuff;
-	apdu.datalen = len;
-	apdu.lc = len;
+		r = sc_transmit_apdu(card, &apdu);
+		LOG_TEST_GOTO_ERR(ctx, r, "APDU transmit failed");
+		r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+		LOG_TEST_GOTO_ERR(ctx, r, "Check SW error");
 
-	r = sc_transmit_apdu(card, &apdu);
-	free(cmdbuff);
-	LOG_TEST_RET(ctx, r, "APDU transmit failed");
+		bytes_left -= to_send;
+		offset += to_send;
+	} while (0 < bytes_left);
 
-	r =  sc_check_sw(card, apdu.sw1, apdu.sw2);
-	LOG_TEST_RET(ctx, r, "Check SW error");
+err:
+	free(cmdbuff);
 
 	LOG_FUNC_RETURN(ctx, count);
 }
 
 
-
 static int sc_hsm_update_binary(sc_card_t *card,
 			       unsigned int idx, const u8 *buf, size_t count,
 			       unsigned long flags)
@@ -1247,7 +1257,7 @@ static int sc_hsm_initialize(sc_card_t *
 		return SC_ERROR_INVALID_ARGUMENTS;
 	}
 	*p++ = 0x81;	// User PIN
-	*p++ = (u8) params->user_pin_len;
+	*p++ = (u8)params->user_pin_len;
 	memcpy(p, params->user_pin, params->user_pin_len);
 	p += params->user_pin_len;
 
@@ -1400,12 +1410,11 @@ static int sc_hsm_unwrap_key(sc_card_t *
 
 	LOG_FUNC_CALLED(card->ctx);
 
-	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_EXT, 0x74, params->key_id, 0x93);
-	apdu.cla = 0x80;
-	apdu.lc = params->wrapped_key_length;
-	apdu.data = params->wrapped_key;
-	apdu.datalen = params->wrapped_key_length;
+	r = sc_hsm_write_ef(card, 0x2F10, 0, params->wrapped_key, params->wrapped_key_length);
+	LOG_TEST_RET(card->ctx, r, "Create EF failed");
 
+	sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x74, params->key_id, 0x93);
+	apdu.cla = 0x80;
 	r = sc_transmit_apdu(card, &apdu);
 	LOG_TEST_RET(ctx, r, "APDU transmit failed");
 
@@ -1597,17 +1606,10 @@ static int sc_hsm_init(struct sc_card *c
 	int flags,ext_flags;
 	sc_file_t *file = NULL;
 	sc_path_t path;
-	sc_hsm_private_data_t *priv = card->drv_data;
+	sc_hsm_private_data_t *priv = NULL;
 
 	LOG_FUNC_CALLED(card->ctx);
 
-	if (!priv) {
-		priv = calloc(1, sizeof(sc_hsm_private_data_t));
-		if (!priv)
-			LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
-		card->drv_data = priv;
-	}
-
 	flags = SC_ALGORITHM_RSA_RAW|SC_ALGORITHM_RSA_PAD_PSS|SC_ALGORITHM_ONBOARD_KEY_GEN;
 
 	_sc_card_add_rsa_alg(card, 1024, flags, 0);
@@ -1639,6 +1641,46 @@ static int sc_hsm_init(struct sc_card *c
 
 	card->caps |= SC_CARD_CAP_RNG|SC_CARD_CAP_APDU_EXT|SC_CARD_CAP_ISO7816_PIN_INFO;
 
+	// APDU Buffer limits
+	//   JCOP 2.4.1r3           1462
+	//   JCOP 2.4.2r3           1454
+	//   JCOP 3                 1232
+	//   MicroSD with JCOP 3    478 / 506 - handled in reader-pcsc.c
+	//   Reiner SCT             1014 - handled in reader-pcsc.c
+
+	// Use JCOP 3 card limits for sending
+	card->max_send_size = 1232;
+	// Assume that card supports sending with extended length APDU and without limit
+	card->max_recv_size = 0;
+
+	if (card->type == SC_CARD_TYPE_SC_HSM_SOC
+			|| card->type == SC_CARD_TYPE_SC_HSM_GOID) {
+		card->max_recv_size = 0x0630;	// SoC Proxy forces this limit
+	} else {
+		// Adjust to the limits set by the reader
+		if (card->reader->max_send_size < card->max_send_size) {
+			if (18 >= card->reader->max_send_size)
+				LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCONSISTENT_CONFIGURATION);
+
+			// 17 byte header and TLV because of odd ins in UPDATE BINARY
+			card->max_send_size = card->reader->max_send_size - 17;
+		}
+
+		if (0 < card->reader->max_recv_size) {
+			if (3 >= card->reader->max_recv_size)
+				LOG_FUNC_RETURN(card->ctx, SC_ERROR_INCONSISTENT_CONFIGURATION);
+			card->max_recv_size = card->reader->max_recv_size - 2;
+		}
+	}
+
+	priv = card->drv_data;
+	if (!priv) {
+		priv = calloc(1, sizeof(sc_hsm_private_data_t));
+		if (!priv)
+			LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY);
+		card->drv_data = priv;
+	}
+
 	sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0);
 	if (sc_hsm_select_file_ex(card, &path, 0, &file) == SC_SUCCESS
 			&& file && file->prop_attr && file->prop_attr_len >= 2) {
@@ -1671,14 +1713,6 @@ static int sc_hsm_init(struct sc_card *c
 	}
 	sc_file_free(file);
 
-	card->max_send_size = 1431;		// 1439 buffer size - 8 byte TLV because of odd ins in UPDATE BINARY
-	if (card->type == SC_CARD_TYPE_SC_HSM_SOC
-			|| card->type == SC_CARD_TYPE_SC_HSM_GOID) {
-		card->max_recv_size = 0x0630;	// SoC Proxy forces this limit
-	} else {
-		card->max_recv_size = 0;		// Card supports sending with extended length APDU and without limit
-	}
-
 	priv->EF_C_DevAut = NULL;
 	priv->EF_C_DevAut_len = 0;
 
@@ -1704,13 +1738,11 @@ static int sc_hsm_finish(sc_card_t * car
 #ifdef ENABLE_SM
 	sc_sm_stop(card);
 #endif
-	if (priv->serialno) {
+	if (priv) {
 		free(priv->serialno);
-	}
-	if (priv->dffcp) {
 		sc_file_free(priv->dffcp);
+		free(priv->EF_C_DevAut);
 	}
-	free(priv->EF_C_DevAut);
 	free(priv);
 
 	return SC_SUCCESS;
Index: opensc-0.19.0/src/libopensc/reader-pcsc.c
===================================================================
--- opensc-0.19.0.orig/src/libopensc/reader-pcsc.c
+++ opensc-0.19.0/src/libopensc/reader-pcsc.c
@@ -270,7 +270,7 @@ static int pcsc_transmit(sc_reader_t *re
 	 * The buffer for the returned data needs to be at least 2 bytes
 	 * larger than the expected data length to store SW1 and SW2. */
 	rsize = rbuflen = apdu->resplen <= 256 ? 258 : apdu->resplen + 2;
-	rbuf     = malloc(rbuflen);
+	rbuf = malloc(rbuflen);
 	if (rbuf == NULL) {
 		r = SC_ERROR_OUT_OF_MEMORY;
 		goto out;
@@ -365,7 +365,7 @@ static int refresh_attributes(sc_reader_
 		if (priv->reader_state.cbAtr > SC_MAX_ATR_SIZE)
 			return SC_ERROR_INTERNAL;
 
-		/* Some cards have a different cold (after a powerup) and warm (after a reset) ATR  */
+		/* Some cards have a different cold (after a powerup) and warm (after a reset) ATR */
 		if (memcmp(priv->reader_state.rgbAtr, reader->atr.value, priv->reader_state.cbAtr) != 0) {
 			reader->atr.len = priv->reader_state.cbAtr;
 			memcpy(reader->atr.value, priv->reader_state.rgbAtr, reader->atr.len);
@@ -482,7 +482,7 @@ static int pcsc_reconnect(sc_reader_t *
 			priv->gpriv->connect_exclusive ? SCARD_SHARE_EXCLUSIVE : SCARD_SHARE_SHARED,
 			protocol, action, &active_proto);
 
-	
+
 	PCSC_TRACE(reader, "SCardReconnect returned", rv);
 	if (rv != SCARD_S_SUCCESS) {
 		PCSC_TRACE(reader, "SCardReconnect failed", rv);
@@ -1165,11 +1165,11 @@ static void detect_reader_features(sc_re
 					sc_log(ctx, "Reader has a display: %04X", caps->wLcdLayout);
 					reader->capabilities |= SC_READER_CAP_DISPLAY;
 				}
-				else   {
+				else {
 					sc_log(ctx, "Reader does not have a display.");
 				}
 			}
-			else   {
+			else {
 				sc_log(ctx,
 						"Returned PIN properties structure has bad length (%lu/%"SC_FORMAT_LEN_SIZE_T"u)",
 						(unsigned long)rcount,
@@ -1198,34 +1198,56 @@ static void detect_reader_features(sc_re
 	reader->max_recv_size = priv->gpriv->force_max_recv_size ?
 		priv->gpriv->force_max_recv_size :
 		SC_READER_SHORT_APDU_MAX_RECV_SIZE;
+
+	size_t max_send_size = 0;
+	size_t max_recv_size = 0;
 	if (priv->get_tlv_properties) {
 		/* Try to set reader max_send_size and max_recv_size based on
 		 * detected max_data */
-		int max_data = part10_detect_max_data(reader, card_handle);
-
-		if (max_data > 0) {
-			sc_log(ctx, "Reader supports transceiving %d bytes of data",
-					max_data);
-			if (priv->gpriv->force_max_send_size)
-				reader->max_send_size = max_data;
-			else
-				sc_log(ctx, "Sending is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data"
-						" in configuration file", reader->max_send_size);
-			if (!priv->gpriv->force_max_recv_size)
-				reader->max_recv_size = max_data;
-			else
-				sc_log(ctx, "Receiving is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data"
-						" in configuration file", reader->max_recv_size);
-		} else {
-			sc_log(ctx, "Assuming that the reader supports transceiving "
-					"short length APDUs only");
-		}
+		max_send_size = max_recv_size = part10_detect_max_data(reader, card_handle);
 
 		/* debug the product and vendor ID of the reader */
 		part10_get_vendor_product(reader, card_handle, NULL, NULL);
 	}
+	else {
+		/* Try to set default limits based on device name */
+		if (!strncmp("REINER SCT cyberJack", reader->name, 20)) {
+			max_send_size = 1014;
+			max_recv_size = 1014;
+		}
+		else if (!strncmp("Secure Flash Card", reader->name, 17)) {
+			max_send_size = 478;
+			max_recv_size = 506;
+		}
+	}
 
-	if(gpriv->SCardGetAttrib != NULL) {
+	if (max_send_size > 0) {
+		sc_log(ctx, "Reader supports sending %"SC_FORMAT_LEN_SIZE_T"u bytes of data",
+				max_send_size);
+		if (!priv->gpriv->force_max_send_size)
+			reader->max_send_size = max_send_size;
+		else
+			sc_log(ctx, "Sending is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data"
+					" in configuration file", reader->max_send_size);
+	} else {
+		sc_log(ctx, "Assuming that the reader supports sending "
+				"short length APDUs only");
+	}
+
+	if (max_recv_size > 0) {
+		sc_log(ctx, "Reader supports receiving %"SC_FORMAT_LEN_SIZE_T"u bytes of data",
+				max_recv_size);
+		if (!priv->gpriv->force_max_recv_size)
+			reader->max_recv_size = max_recv_size;
+		else
+			sc_log(ctx, "Receiving is limited to %"SC_FORMAT_LEN_SIZE_T"u bytes of data"
+					" in configuration file", reader->max_recv_size);
+	} else {
+		sc_log(ctx, "Assuming that the reader supports receiving "
+				"short length APDUs only");
+	}
+
+	if (gpriv->SCardGetAttrib != NULL) {
 		rcount = sizeof(rbuf);
 		if (gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_NAME,
 					rbuf, &rcount) == SCARD_S_SUCCESS
@@ -1236,7 +1258,7 @@ static void detect_reader_features(sc_re
 		}
 
 		rcount = sizeof i;
-		if(gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_IFD_VERSION,
+		if (gpriv->SCardGetAttrib(card_handle, SCARD_ATTR_VENDOR_IFD_VERSION,
 					(u8 *) &i, &rcount) == SCARD_S_SUCCESS
 				&& rcount == sizeof i) {
 			reader->version_major = (i >> 24) & 0xFF;
@@ -1246,7 +1268,7 @@ static void detect_reader_features(sc_re
 }
 
 int pcsc_add_reader(sc_context_t *ctx,
-	   	char *reader_name, size_t reader_name_len,
+		char *reader_name, size_t reader_name_len,
 		sc_reader_t **out_reader)
 {
 	int ret = SC_ERROR_INTERNAL;
@@ -1461,7 +1483,7 @@ static int pcsc_wait_for_event(sc_contex
 
 	LOG_FUNC_CALLED(ctx);
 
-	if (!event_reader && !event && reader_states)   {
+	if (!event_reader && !event && reader_states) {
 		sc_log(ctx, "free allocated reader states");
 		free(*reader_states);
 		*reader_states = NULL;
@@ -1751,7 +1773,7 @@ static int part10_build_modify_pin_block
 	sc_apdu_t *apdu = data->apdu;
 	u8 tmp;
 	unsigned int tmp16;
-	PIN_MODIFY_STRUCTURE *pin_modify  = (PIN_MODIFY_STRUCTURE *)buf;
+	PIN_MODIFY_STRUCTURE *pin_modify = (PIN_MODIFY_STRUCTURE *)buf;
 	struct sc_pin_cmd_pin *pin_ref =
 		data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ?
 		&data->pin2 : &data->pin1;
@@ -2330,7 +2352,7 @@ int pcsc_use_reader(sc_context_t *ctx, v
 	sc_log(ctx, "Probing PC/SC reader");
 
 	gpriv->pcsc_ctx = *(SCARDCONTEXT *)pcsc_context_handle;
-	card_handle =  *(SCARDHANDLE *)pcsc_card_handle;
+	card_handle = *(SCARDHANDLE *)pcsc_card_handle;
 
 	if(SCARD_S_SUCCESS == gpriv->SCardGetAttrib(card_handle,
 				SCARD_ATTR_DEVICE_SYSTEM_NAME_A, (LPBYTE)
