Dear Emanuele,

attached is the patch I had written about. It works if the following
three conditions a met:

1. If on the pkcs15 level a key is known as usable for signing and
decryption, it must be generated in a way that:
        * the card can use it to perform PSO_DEC
        * the card doesn't add any padding when performing PSO_DEC
          using this key

2. If on the pkcs15 level a key is known as usable for signing only, it
must be generated in a way that:
        * the card can use it to perform PSO_CDS
        * the card adds pkcs1 padding when performing PSO_CDS using
          this key

3. If on the pkcs15 level a key is known as usable for decryption only,
it must be generated in a way that:
        * the card can use it to perform PSO_DEC
        * the card adds pkcs1 padding when performing PSO_DEC using
          this key

> > it would be nice, if you could provide some more information about the
> > card you are working on. What I'm interested in is: If there are keys on
> > the card which are usable for signing but not for decrypting or vice
> > versa (in context of pkcs11/15)? And if so, is the pkcs1 padding for
> > this keys is added/removed by the card or is it done in the library?
> 
> Yes. The main key can be used by policy for both signing and
> encryption/decryption, but the card won't accept the CDS command, so
> that at the driver level, the key can only be used for
> encryption/decryption.
> 
> IIRC the padding is performed by the library; the card blindly
> encrypts the given block (but, alawys IIRC, does require that the
> block is PKCS #1 compliant).

It seems to me, that your key_specs fulfil the above requirements. But
keep in mind, that this patch was developed for cardos 4.3b and is only
tested with those type of card. So I would guess, that you have to do
some adjustments to get it working for your card too. Your questions are
welcome.

Another point is, that the integration isn't very nice yet. If you are
willing to improve this, please let me know.

Best Regards
Andre


Index: types.h
===================================================================
--- types.h	(revision 4593)
+++ types.h	(working copy)
@@ -27,6 +27,9 @@
 
 typedef unsigned char u8;
 
+#define SC_TRUE   1
+#define SC_FALSE  0
+
 /* various maximum values */
 #define SC_MAX_READER_DRIVERS		6
 #define SC_MAX_READERS			16
Index: pkcs15-sec.c
===================================================================
--- pkcs15-sec.c	(revision 4593)
+++ pkcs15-sec.c	(working copy)
@@ -118,6 +118,7 @@
 		}
 	}
 
+	senv.prkey = prkey;
 	r = sc_set_security_env(p15card->card, &senv, 0);
 	if (r < 0) {
 		sc_unlock(p15card->card);
@@ -280,6 +281,7 @@
 		}
 	}
 
+	senv.prkey = prkey;
 	r = sc_set_security_env(p15card->card, &senv, 0);
 	if (r < 0) {
 		sc_unlock(p15card->card);
Index: card-cardos.c
===================================================================
--- card-cardos.c	(revision 4593)
+++ card-cardos.c	(working copy)
@@ -40,6 +40,12 @@
 	NULL, 0, NULL
 };
 
+typedef struct cardos_senv_state {
+	u8 sign_with_decipher;
+	u8 padding_striped_by_token;
+	size_t key_len;
+} cardos_senv_state_t;
+
 static struct sc_atr_table cardos_atrs[] = {
 	/* 4.0 */
 	{ "3b:e2:00:ff:c1:10:31:fe:55:c8:02:9c", NULL, NULL, SC_CARD_TYPE_CARDOS_GENERIC, 0, NULL },
@@ -151,17 +157,17 @@
 
 static int cardos_init(sc_card_t *card)
 {
-	unsigned long	flags;
+	unsigned long flags = 0;
 
 	card->name = "CardOS M4";
 	card->cla = 0x00;
 
 	/* Set up algorithm info. */
-	flags = SC_ALGORITHM_NEED_USAGE
-		| SC_ALGORITHM_RSA_RAW
+	flags = SC_ALGORITHM_RSA_PAD_PKCS1
 		| SC_ALGORITHM_RSA_HASH_NONE
 		| SC_ALGORITHM_ONBOARD_KEY_GEN
 		;
+
 	_sc_card_add_rsa_alg(card,  512, flags, 0);
 	_sc_card_add_rsa_alg(card,  768, flags, 0);
 	_sc_card_add_rsa_alg(card, 1024, flags, 0);
@@ -177,8 +183,9 @@
 		|| card->type == SC_CARD_TYPE_CARDOS_M4_2B
 		|| card->type == SC_CARD_TYPE_CARDOS_M4_2C
 		|| card->type == SC_CARD_TYPE_CARDOS_M4_4) {
-		card->caps |= SC_CARD_CAP_RSA_2048;
 		card->caps |= SC_CARD_CAP_APDU_EXT;
+		card->caps |= SC_CARD_CAP_RNG;
+		card->caps |= SC_CARD_CAP_RSA_2048;
 	}
 
 	if (card->caps & SC_CARD_CAP_RSA_2048) {
@@ -188,6 +195,8 @@
 		_sc_card_add_rsa_alg(card, 2048, flags, 0);
 	}
 
+	card->drv_data = malloc(sizeof(cardos_senv_state_t));
+
 	return 0;
 }
 
@@ -703,173 +712,125 @@
 	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
 }
 
-/*
- * Set the security context
- * Things get a little messy here. It seems you cannot do any
- * crypto without a security environment - but there isn't really
- * a way to specify the security environment in PKCS15.
- * What I'm doing here (for now) is to assume that for a key
- * object with ID 0xNN there is always a corresponding SE object
- * with the same ID.
- * XXX Need to find out how the Aladdin drivers do it.
- */
 static int
-cardos_set_security_env(sc_card_t *card,
-			    const sc_security_env_t *env,
-			    int se_num)
+cardos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num)
 {
-	sc_apdu_t apdu;
-	u8	data[3];
-	int	key_id, r;
+	assert(card != NULL && env != NULL && se_num == 0);
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
 
-	assert(card != NULL && env != NULL);
+	cardos_senv_state_t *senv_state = (cardos_senv_state_t *) card->drv_data;
+	sc_security_env_t tmp = *env;
+	int r;
 
-	if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT)
-	 || env->key_ref_len != 1) {
-		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No or invalid key reference\n");
-		return SC_ERROR_INVALID_ARGUMENTS;
-	}
-	key_id = env->key_ref[0];
+	if (env->operation != SC_SEC_OPERATION_DECIPHER && env->operation != SC_SEC_OPERATION_SIGN)
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NOT_SUPPORTED);
 
-	sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0);
-	if (card->type == SC_CARD_TYPE_CARDOS_CIE_V1) {
-		cardos_restore_security_env(card, 0x30);
-		apdu.p1 = 0xF1;
-	} else {
-		apdu.p1 = 0x01;
-	}
-	switch (env->operation) {
-	case SC_SEC_OPERATION_DECIPHER:
-		apdu.p2 = 0xB8;
-		break;
-	case SC_SEC_OPERATION_SIGN:
-		apdu.p2 = 0xB6;
-		break;
-	default:
-		return SC_ERROR_INVALID_ARGUMENTS;
-	}
+	if (!(env->flags & SC_SEC_ENV_KEY_REF_PRESENT && env->key_ref_len == 1))
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
 
-	data[0] = 0x83;
-	data[1] = 0x01;
-	data[2] = key_id;
-	apdu.lc = apdu.datalen = 3;
-	apdu.data = data;
+	if (env->operation == SC_SEC_OPERATION_SIGN &&
+		env->prkey->usage & (SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP))
+		tmp.operation = SC_SEC_OPERATION_DECIPHER;
+	tmp.flags &= ~(SC_SEC_ENV_ALG_REF_PRESENT | SC_SEC_ENV_FILE_REF_PRESENT);
 
-	r = sc_transmit_apdu(card, &apdu);
-	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
+	r = iso_ops->set_security_env(card, &tmp, 0);
 
-	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
-	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Card returned error");
+	if (r == SC_SUCCESS) {
+		// some information about the current security environment is needed
+		// by the actual sign and decipher ops, thus keep it handy
+		if (env->operation == SC_SEC_OPERATION_DECIPHER) {
+			senv_state->padding_striped_by_token = (env->prkey->usage & (
+				SC_PKCS15_PRKEY_USAGE_SIGN |
+				SC_PKCS15_PRKEY_USAGE_SIGNRECOVER |
+				SC_PKCS15_PRKEY_USAGE_NONREPUDIATION)) ? SC_FALSE : SC_TRUE;
+			senv_state->key_len = (env->prkey->modulus_length / 8) +
+							(env->prkey->modulus_length % 8 ? 1 : 0);
+		}
+		if (env->operation == SC_SEC_OPERATION_SIGN) {
+			senv_state->sign_with_decipher = (env->operation != tmp.operation) ? SC_TRUE : SC_FALSE;
+			senv_state->key_len = (env->prkey->modulus_length / 8) +
+							(env->prkey->modulus_length % 8 ? 1 : 0);
+		}
+	}
 
-	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
 }
 
-/*
- * Compute digital signature
- */
-
-/* internal function to do the actual signature computation */
 static int
-do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
-		     u8 *out, size_t outlen)
+cardos_decipher(sc_card_t *card, const u8 *crgram, size_t crgram_len, u8 *out, size_t out_len)
 {
+	assert(card != NULL && crgram != NULL && out != NULL);
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	cardos_senv_state_t *senv_state = (cardos_senv_state_t *) card->drv_data;
+	size_t key_len = senv_state->key_len;
+	char rbuf[SC_MAX_APDU_BUFFER_SIZE];
 	int r;
-	sc_apdu_t apdu;
 
-	/* INS: 0x2A  PERFORM SECURITY OPERATION
-	 * P1:  0x9E  Resp: Digital Signature
-	 * P2:  0x9A  Cmd: Input for Digital Signature */
-	sc_format_apdu(card, &apdu, SC_APDU_CASE_4, 0x2A, 0x9E, 0x9A);
-	apdu.resp    = out;
-	apdu.le      = outlen;
-	apdu.resplen = outlen;
+	if (sizeof(rbuf) < key_len)
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
 
-	apdu.data    = data;
-	apdu.lc      = datalen;
-	apdu.datalen = datalen;
-	r = sc_transmit_apdu(card, &apdu);
-	SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed");
+	if (crgram_len != key_len)
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_WRONG_LENGTH);
 
-	if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
-		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, apdu.resplen);
-	else
-		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2));
+	r = iso_ops->decipher(card, crgram, crgram_len, rbuf, sizeof(rbuf));
+
+	if (r < 1 || r > key_len)
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+
+	if (senv_state->padding_striped_by_token == SC_TRUE)
+		if (r > out_len) {
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_BUFFER_TOO_SMALL);
+		} else {
+			memcpy(out, rbuf, r);
+		}
+
+	if (senv_state->padding_striped_by_token == SC_FALSE)
+		if (r != key_len) {
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INTERNAL);
+		} else {
+			r = sc_pkcs1_strip_02_padding(rbuf, r, out, &out_len);
+		}
+
+	memset(rbuf, 0, sizeof(rbuf));
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
 }
 
 static int
-cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen,
-			 u8 *out, size_t outlen)
+cardos_compute_signature(sc_card_t *card, const u8 *crgram, size_t crgram_len, u8 *out, size_t out_len)
 {
-	int    r;
-	u8     buf[SC_MAX_APDU_BUFFER_SIZE];
-	size_t buf_len = sizeof(buf), tmp_len = buf_len;
-	sc_context_t *ctx;
+	assert(card != NULL && crgram != NULL && out != NULL);
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
 
-	assert(card != NULL && data != NULL && out != NULL);	
-	ctx = card->ctx;
-	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
+	cardos_senv_state_t *senv_state = (cardos_senv_state_t *) card->drv_data;
+	size_t key_len = senv_state->key_len;
+        u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
+	int r;
 
-	if (datalen > SC_MAX_APDU_BUFFER_SIZE)
-		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
-	if (outlen < datalen)
+	if (crgram_len < 1 || crgram_len > key_len - 11)
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_WRONG_LENGTH);
+
+	if (out_len < key_len)
 		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_BUFFER_TOO_SMALL);
-	outlen = datalen;
 
-	/* XXX As we don't know what operations are allowed with a
-	 * certain key, let's try RSA_PURE etc. and see which operation
-	 * succeeds (this is not really beautiful, but currently the
-	 * only way I see) -- Nils
-	 *
-	 * We also check for several caps flags here to pervent generating
-	 * invalid signatures with duplicated hash prefixes with some cards
-	 */
-
-        if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED)
-            sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Forcing RAW_HASH_STRIPPED\n");        	 
-        if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH)
-            sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Forcing RAW_HASH\n");
-
-	if (!(card->caps & (SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED | SC_CARD_CAP_ONLY_RAW_HASH))) {
-		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying RSA_PURE_SIG (padded DigestInfo)\n");
-		r = do_compute_signature(card, data, datalen, out, outlen);
-		if (r >= SC_SUCCESS)
-			SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r);
-	}		
-		
-	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying RSA_SIG (just the DigestInfo)\n");
-	/* remove padding: first try pkcs1 bt01 padding */
-	r = sc_pkcs1_strip_01_padding(data, datalen, buf, &tmp_len);
-	if (r != SC_SUCCESS) {
-		const u8 *p = data;
-		/* no pkcs1 bt01 padding => let's try zero padding
-		 * This can only work if the data tbs doesn't have a
-		 * leading 0 byte.  */
-		tmp_len = buf_len;
-		while (*p == 0 && tmp_len != 0) {
-			++p;
-			--tmp_len;
-		}
-		memcpy(buf, p, tmp_len);
+	if (senv_state->sign_with_decipher) {
+		if (sizeof(sbuf) < key_len)
+	                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
+		// add 01 padding and let token sign in raw mode
+		sbuf[0] = 0;
+		sbuf[1] = 1;
+		memset(sbuf + 2, 0xFF, key_len - crgram_len - 3);
+		sbuf[key_len - crgram_len - 1] = 0;
+		memcpy(sbuf + key_len - crgram_len, crgram, crgram_len);
+		r = iso_ops->decipher(card, sbuf, key_len, out, out_len);
+	} else {
+		// token will add 01 padding
+		r = iso_ops->compute_signature(card, crgram, crgram_len, out, out_len);
 	}
 
-	if (!(card->caps & (SC_CARD_CAP_ONLY_RAW_HASH_STRIPPED | SC_CARD_CAP_ONLY_RAW_HASH)) || card->caps & SC_CARD_CAP_ONLY_RAW_HASH ) {
-		sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying to sign raw hash value with prefix\n");	
-		r = do_compute_signature(card, buf, tmp_len, out, outlen);
-		if (r >= SC_SUCCESS)	
-			SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r);
-	}
-
-	if (card->caps & SC_CARD_CAP_ONLY_RAW_HASH) {
-	    sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "Failed to sign raw hash value with prefix when forcing\n");
-	    SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_INVALID_ARGUMENTS);
-	}
-	   
-	sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "trying to sign stripped raw hash value (card is responsible for prefix)\n");
-	r = sc_pkcs1_strip_digest_info_prefix(NULL,buf,tmp_len,buf,&buf_len);
-	if (r != SC_SUCCESS)
-		SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_VERBOSE, r);
-	return do_compute_signature(card, buf, buf_len, out, outlen);
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
 }
+	
 
 static int
 cardos_lifecycle_get(sc_card_t *card, int *mode)
@@ -1179,6 +1140,7 @@
 	cardos_ops.create_file = cardos_create_file;
 	cardos_ops.set_security_env = cardos_set_security_env;
 	cardos_ops.restore_security_env = cardos_restore_security_env;
+	cardos_ops.decipher = cardos_decipher;
 	cardos_ops.compute_signature = cardos_compute_signature;
 
 	cardos_ops.list_files = cardos_list_files;
Index: opensc.h
===================================================================
--- opensc.h	(revision 4593)
+++ opensc.h	(working copy)
@@ -132,7 +132,7 @@
 	struct sc_path file_ref;
 	u8 key_ref[8];
 	size_t key_ref_len;
-
+	struct sc_pkcs15_prkey_info *prkey; /* prkey->usage + prkey->modulus_length is needed by cardos driver */
 	struct sc_supported_algo_info supported_algos[SC_MAX_SUPPORTED_ALGORITHMS];
 } sc_security_env_t;
 
_______________________________________________
opensc-devel mailing list
[email protected]
http://www.opensc-project.org/mailman/listinfo/opensc-devel

Reply via email to