Репозиторий Sisyphus
Последнее обновление: 1 октября 2023 | Пакетов: 18631 | Посещений: 37728133
en ru br
Репозитории ALT

Группа :: Система/Настройка/Оборудование
Пакет: gnupg-pkcs11-scd

 Главная   Изменения   Спек   Патчи   Sources   Загрузить   Gear   Bugs and FR  Repocop 

Патч: gnupg-pkcs11-scd-0.9.2-gost.patch
Скачать


 gnupg-pkcs11-scd/command.c          | 488 +++++++++++++++++++++++++-----------
 gnupg-pkcs11-scd/common.c           |  29 +++
 gnupg-pkcs11-scd/common.h           |   5 +
 gnupg-pkcs11-scd/gnupg-pkcs11-scd.1 |   4 +-
 gnupg-pkcs11-scd/keyutil.c          | 347 +++++++++++++++++++------
 gnupg-pkcs11-scd/keyutil.h          |  29 ++-
 gnupg-pkcs11-scd/scdaemon.c         |  42 +++-
 7 files changed, 710 insertions(+), 234 deletions(-)
diff --git a/gnupg-pkcs11-scd/command.c b/gnupg-pkcs11-scd/command.c
index 331a005..57ca2d9 100644
--- a/gnupg-pkcs11-scd/command.c
+++ b/gnupg-pkcs11-scd/command.c
@@ -422,7 +422,7 @@ send_certificate_list (
 		if (
 			(error = assuan_write_status (
 				ctx,
-				"KEY-FRIEDNLY",
+				"KEY-FRIENDLY",
 				nameinfo
 			)) != GPG_ERR_NO_ERROR
 		) {
@@ -515,10 +515,6 @@ send_certificate_list (
 			free (nameinfo);
 			nameinfo = NULL;
 		}
-
-		if (error != GPG_ERR_NO_ERROR) {
-			goto cleanup;
-		}
 	}
 
 	error = GPG_ERR_NO_ERROR;
@@ -974,6 +970,64 @@ cleanup:
 	return gpg_error (error);
 }
 
+#define NSSCK_VENDOR_PKCS11_RU_TEAM 0xD4321000 /* 0x80000000 | 0x54321000 */
+#define CK_VENDOR_PKCS11_RU_TEAM_TC26 NSSCK_VENDOR_PKCS11_RU_TEAM
+
+#define CKM_GOSTR3410_KEY_PAIR_GEN		0x00001200
+#define CKM_GOSTR3410					0x00001201
+#define CKM_GOSTR3410_WITH_GOSTR3411	0x00001202
+#define CKM_GOSTR3410_KEY_WRAP			0x00001203
+#define CKM_GOSTR3410_DERIVE			0x00001204
+#define CKM_GOSTR3411					0x00001210
+#define CKM_GOSTR3411_HMAC				0x00001211
+#define CKM_GOST28147_KEY_GEN			0x00001220
+#define CKM_GOST28147_ECB				0x00001221
+#define CKM_GOST28147					0x00001222
+#define CKM_GOST28147_MAC				0x00001223
+#define CKM_GOST28147_KEY_WRAP			0x00001224
+#define CKM_GOSTR3410_512               (CK_VENDOR_PKCS11_RU_TEAM_TC26 | 0x006)
+#define CKM_GOSTR3410_12_DERIVE         (CK_VENDOR_PKCS11_RU_TEAM_TC26 |0x007)
+
+static key_subtype_t
+get_key_subtype(assuan_context_t ctx,
+                pkcs11h_certificate_id_t cert_id)
+{
+	unsigned char *blob = NULL;
+	size_t blob_size;
+	gpg_err_code_t error = GPG_ERR_GENERAL;
+	cert_params_t *params = NULL;
+    key_subtype_t ret = KEY_UNKNOWN;
+
+	if (
+		(error = get_cert_blob (
+			ctx,
+			cert_id,
+			&blob,
+			&blob_size
+		)) != GPG_ERR_NO_ERROR ||
+		(error = keyutil_get_cert_params (
+			blob,
+			blob_size,
+			&params
+		)) != GPG_ERR_NO_ERROR
+	) {
+		goto cleanup;
+	}
+
+    ret = params->key_subtype;
+
+ cleanup:
+
+	keyutil_params_free (params);
+
+	if (blob != NULL) {
+		free (blob);
+		blob = NULL;
+	}
+
+	return ret;
+}
+
 /** Sign data (set by SETDATA) with certificate id in line. */
 gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 {
@@ -1002,12 +1056,19 @@ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 		{ 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
 		0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
 		0x00, 0x04, 0x40  };
+	static const unsigned char gost94_prefix[] = /* (1.2.643.2.2.3) */
+		{ 0x2a, 0x85, 0x03, 0x02, 0x02, 0x03 };
+	static const unsigned char gost12_256_prefix[] = /* (1.2.643.7.1.1.3.2) */
+		{ 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x02 };
+	static const unsigned char gost12_512_prefix[] = /* (1.2.643.7.1.1.3.3) */
+		{ 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x03 };
 
 	gpg_err_code_t error = GPG_ERR_GENERAL;
 	pkcs11h_certificate_id_t cert_id = NULL;
 	pkcs11h_certificate_t cert = NULL;
 	cmd_data_t *data = (cmd_data_t *)assuan_get_pointer (ctx);
-	cmd_data_t *_data = data;
+	cmd_data_t data_buf = *data;
+	cmd_data_t *_data = &data_buf;
 	int need_free__data = 0;
 	int session_locked = 0;
 	unsigned char *sig = NULL;
@@ -1023,6 +1084,16 @@ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 		INJECT_SHA384,
 		INJECT_SHA512
 	} inject = INJECT_NONE;
+	enum {
+		REJECT_NONE,
+		REJECT_GOST94,
+		REJECT_GOST12_256,
+		REJECT_GOST12_512
+	} reject = REJECT_NONE;
+    enum {
+        REVERSE_NONE,
+        REVERSE_BYTES
+    } reverse = REVERSE_NONE;
 
 	if (data->data == NULL) {
 		error = GPG_ERR_INV_DATA;
@@ -1054,6 +1125,36 @@ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 		error = GPG_ERR_INV_DATA;
 		goto cleanup;
 	}
+
+	if (
+		(error = _get_certificate_by_name (
+			ctx,
+			line,
+			OPENPGP_SIGN,
+			&cert_id,
+			NULL
+		)) != GPG_ERR_NO_ERROR
+	) {
+		goto cleanup;
+	}
+
+	CK_MECHANISM_TYPE mech;
+    switch (get_key_subtype (ctx, cert_id)) {
+    case KEY_RSA_RSA:
+		mech = CKM_RSA_PKCS;
+		break;
+	case KEY_ECC_GOST2001:
+	case KEY_ECC_GOST2012_256:
+		mech = CKM_GOSTR3410;
+		break;
+	case KEY_ECC_GOST2012_512:
+		mech = CKM_GOSTR3410_512;
+		break;
+	default:
+		error = GPG_ERR_UNSUPPORTED_ALGORITHM;
+		goto cleanup;
+	}
+	
 	/*
 	 * sender prefixed data with algorithm OID
 	 */
@@ -1114,26 +1215,50 @@ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 		}
 	}
 	else {
-		if (
-			data->size == 0x10 + sizeof (md5_prefix) ||
-			data->size == 0x14 + sizeof (sha1_prefix) ||
-			data->size == 0x14 + sizeof (rmd160_prefix)
-		) {
+		switch (mech) {
+		case CKM_GOSTR3410:
+        case CKM_GOSTR3410_512:
+			inject = INJECT_NONE;
+            reverse = REVERSE_BYTES;
 			if (
-				memcmp (data->data, md5_prefix, sizeof (md5_prefix)) &&
-				memcmp (data->data, sha1_prefix, sizeof (sha1_prefix)) &&
-				memcmp (data->data, rmd160_prefix, sizeof (rmd160_prefix))
+				0 == memcmp (data->data, gost94_prefix,
+							  sizeof (gost94_prefix))
 			) {
-				error = GPG_ERR_UNSUPPORTED_ALGORITHM;
-				goto cleanup;
+				reject = REJECT_GOST94;
+			} else if (
+				0 == memcmp (data->data, gost12_256_prefix,
+							  sizeof (gost12_256_prefix))
+			) {
+				reject = REJECT_GOST12_256;
+			} else if (
+				0 == memcmp (data->data, gost12_512_prefix,
+							  sizeof (gost12_512_prefix))
+			) {
+				reject = REJECT_GOST12_512;
+			}
+			break;
+		default:
+			if (
+				data->size == 0x10 + sizeof (md5_prefix) ||
+				data->size == 0x14 + sizeof (sha1_prefix) ||
+				data->size == 0x14 + sizeof (rmd160_prefix)
+			) {
+				if (
+					memcmp (data->data, md5_prefix, sizeof (md5_prefix)) &&
+					memcmp (data->data, sha1_prefix, sizeof (sha1_prefix)) &&
+					memcmp (data->data, rmd160_prefix, sizeof (rmd160_prefix))
+				) {
+					error = GPG_ERR_UNSUPPORTED_ALGORITHM;
+					goto cleanup;
+				}
+			}
+			else {
+				/*
+				 * unknown hash algorithm;
+				 * gnupg's scdaemon forces to SHA1
+				 */
+				inject = INJECT_SHA1;
 			}
-		}
-		else {
-			/*
-			 * unknown hash algorithm;
-			 * gnupg's scdaemon forces to SHA1
-			 */
-			inject = INJECT_SHA1;
 		}
 	}
 
@@ -1174,17 +1299,12 @@ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 				goto cleanup;
 		}
 
-		need_free__data = 1;
-
-		if ((_data = (cmd_data_t *)malloc (sizeof (cmd_data_t))) == NULL) {
-			error = GPG_ERR_ENOMEM;
-			goto cleanup;
-		}
-
 		if ((_data->data = (unsigned char *)malloc (data->size + oid_size)) == NULL) {
 			error = GPG_ERR_ENOMEM;
 			goto cleanup;
 		}
+		
+		need_free__data = 1;
 
 		_data->size = 0;
 		memmove (_data->data+_data->size, oid, oid_size);
@@ -1193,17 +1313,53 @@ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 		_data->size += data->size;
 	}
 
-	if (
-		(error = _get_certificate_by_name (
-			ctx,
-			line,
-			OPENPGP_SIGN,
-			&cert_id,
-			NULL
-		)) != GPG_ERR_NO_ERROR
-	) {
-		goto cleanup;
-	}
+	if (reject != REJECT_NONE) {
+		size_t oid_size;
+		switch (reject) {
+		case REJECT_GOST94:
+			oid_size = sizeof(gost94_prefix);
+			break;
+		case REJECT_GOST12_256:
+			oid_size = sizeof(gost12_256_prefix);
+			break;
+		case REJECT_GOST12_512:
+			oid_size = sizeof(gost12_512_prefix);
+			break;
+		default:
+			error = GPG_ERR_INV_DATA;
+			goto cleanup;
+		}
+
+        if (need_free__data) {
+            memmove (_data->data, _data->data + oid_size,
+                     _data->size - oid_size);
+        } else {
+            _data->data = _data->data + oid_size;
+        }
+        _data->size = _data->size - oid_size;
+	}
+
+    if (reverse != REVERSE_NONE) {
+        if (!need_free__data) {
+            unsigned char *src_data = _data->data;
+            _data->data = (unsigned char *) malloc (_data->size);
+            if (!_data->data) {
+                error = GPG_ERR_ENOMEM;
+                goto cleanup;
+            }
+            need_free__data = 1;
+            memmove (_data->data, src_data, _data->size);
+        }
+
+        switch (reverse) {
+        case REVERSE_BYTES:
+            reverse_buffer (_data->data, _data->size);
+            break;
+        default:
+            error = GPG_ERR_INV_DATA;
+            goto cleanup;
+        }
+    }
 
 	if (
 		(error = common_map_pkcs11_error (
@@ -1227,12 +1383,12 @@ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 		goto cleanup;
 	}
 	session_locked = 1;
-
+	
 	if (
 		(error = common_map_pkcs11_error (
 			pkcs11h_certificate_signAny (
 				cert,
-				CKM_RSA_PKCS,
+				mech,
 				_data->data,
 				_data->size,
 				NULL,
@@ -1252,7 +1408,7 @@ gpg_error_t cmd_pksign (assuan_context_t ctx, char *line)
 		(error = common_map_pkcs11_error (
 			pkcs11h_certificate_signAny (
 				cert,
-				CKM_RSA_PKCS,
+				mech,
 				_data->data,
 				_data->size,
 				sig,
@@ -1291,8 +1447,7 @@ cleanup:
 	if (need_free__data) {
 		free (_data->data);
 		_data->data = NULL;
-		free (_data);
-		_data = NULL;
+		_data->size = 0;
 	}
 
 	return gpg_error (error);
@@ -1318,24 +1473,8 @@ gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line)
 		goto cleanup;
 	}
 
-	/*
-	 * Guess.. taken from openpgp card implementation
-	 * and java PKCS#11 provider.
-	 */
 	_data.data = data->data;
 	_data.size = data->size;
-	if (
-		*_data.data == 0 && (
-			_data.size == 129 ||
-			_data.size == 193 ||
-			_data.size == 257 ||
-			_data.size == 385 ||
-			_data.size == 513
-		)
-	) {
-		_data.data++;
-		_data.size--;
-	}
 
 	if (
 		(error = _get_certificate_by_name (
@@ -1349,6 +1488,61 @@ gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line)
 		goto cleanup;
 	}
 
+	CK_MECHANISM_TYPE mech;
+    unsigned int key_len = 0;
+    switch (get_key_subtype (ctx, cert_id)) {
+    case KEY_RSA_RSA:
+		mech = CKM_RSA_PKCS;
+		break;
+	case KEY_ECC_GOST2001:
+		mech = CKM_GOSTR3410_DERIVE;
+		key_len = 64;
+		break;
+	case KEY_ECC_GOST2012_256:
+        mech = CKM_GOSTR3410_12_DERIVE;
+        key_len = 64;
+        break;
+	case KEY_ECC_GOST2012_512:
+        mech = CKM_GOSTR3410_12_DERIVE;
+        key_len = 128;
+        break;
+	default:
+		error = GPG_ERR_UNSUPPORTED_ALGORITHM;
+		goto cleanup;
+	}
+
+	switch (mech) {
+    case CKM_GOSTR3410_DERIVE:
+    case CKM_GOSTR3410_12_DERIVE:
+			if (_data.size % 2 && _data.data[0] == 0x04) {
+				// Uncompressed point
+				_data.data++;
+				_data.size--;
+			}
+			/* Reverse byte order of each value: X Y UKM */
+			reverse_buffer (_data.data, key_len/2);
+			reverse_buffer (_data.data + key_len/2, key_len/2);
+			reverse_buffer (_data.data + key_len, _data.size - key_len);
+			break;
+    default:
+		/*
+		 * Guess.. taken from openpgp card implementation
+		 * and java PKCS#11 provider.
+		 */
+		if (
+			*_data.data == 0 && (
+				 _data.size == 129 ||
+				 _data.size == 193 ||
+				 _data.size == 257 ||
+				 _data.size == 385 ||
+				 _data.size == 513
+			)
+		) {
+			_data.data++;
+			_data.size--;
+		}
+	}
+
 	if (
 		(error = common_map_pkcs11_error (
 			pkcs11h_certificate_create (
@@ -1372,19 +1566,26 @@ gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line)
 	}
 	session_locked = 1;
 
-	if (
-		(error = common_map_pkcs11_error (
-			pkcs11h_certificate_decryptAny (
-				cert,
-				CKM_RSA_PKCS, 
-				_data.data,
-				_data.size,
-				NULL,
-				&ptext_len
-			)
-		)) != GPG_ERR_NO_ERROR
-	) {
-		goto cleanup;
+	switch ( mech ) {
+	case CKM_GOSTR3410_DERIVE:
+	case CKM_GOSTR3410_12_DERIVE:
+		ptext_len = key_len;
+		break;
+	default:
+		if (
+			(error = common_map_pkcs11_error (
+				pkcs11h_certificate_decryptAny (
+					cert,
+					mech,
+					_data.data,
+					_data.size,
+					NULL,
+					&ptext_len
+				)
+			)) != GPG_ERR_NO_ERROR
+		) {
+			goto cleanup;
+		}
 	}
 
 	if ((ptext = (unsigned char *)malloc (ptext_len)) == NULL) {
@@ -1396,7 +1597,7 @@ gpg_error_t cmd_pkdecrypt (assuan_context_t ctx, char *line)
 		(error = common_map_pkcs11_error (
 			pkcs11h_certificate_decryptAny (
 				cert,
-				CKM_RSA_PKCS, 
+				mech,
 				_data.data,
 				_data.size,
 				ptext,
@@ -1674,12 +1875,10 @@ gpg_error_t cmd_genkey (assuan_context_t ctx, char *line)
 {
 	gpg_err_code_t error = GPG_ERR_GENERAL;
 	pkcs11h_certificate_id_t cert_id = NULL;
-	gcry_mpi_t n_mpi = NULL;
-	gcry_mpi_t e_mpi = NULL;
-	unsigned char *n_hex = NULL;
-	unsigned char *e_hex = NULL;
-	char *n_resp = strdup ("n ");
-	char *e_resp = strdup ("e ");
+	cert_params_t *params = NULL;
+	unsigned char *hex[] = {NULL, NULL, NULL};
+	char *resp[] = {NULL, NULL, NULL};
+	int i, count = 0;
 	unsigned char *blob = NULL;
 	char *serial = NULL;
 	char *key = NULL;
@@ -1758,94 +1957,82 @@ gpg_error_t cmd_genkey (assuan_context_t ctx, char *line)
 			&blob,
 			&blob_size
 		)) != GPG_ERR_NO_ERROR ||
-		(error = keyutil_get_cert_mpi (
+		(error = keyutil_get_cert_params (
 			blob,
 			blob_size,
-			&n_mpi,
-			&e_mpi
+			&params
 		)) != GPG_ERR_NO_ERROR
 	) {
 		goto cleanup;
 	}
 
-	if (
-		gcry_mpi_aprint (
-			GCRYMPI_FMT_HEX,
-			&n_hex,
-			NULL,
-			n_mpi
-		) ||
-		gcry_mpi_aprint (
-			GCRYMPI_FMT_HEX,
-			&e_hex,
-			NULL,
-			e_mpi
-		)
-	) {
+	switch (params->key_type) {
+	case KEY_RSA:
+		resp[0] = strdup ("n ");
+		resp[1] = strdup ("e ");
+		count = 2;
+		break;
+	case KEY_ECC:
+		resp[0] = strdup ("X ");
+		resp[1] = strdup ("Y ");
+		resp[2] = strdup ("Z ");
+		count = 3;
+		break;
+	default:
 		error = GPG_ERR_BAD_KEY;
 		goto cleanup;
 	}
 
-	if (
-		!encoding_strappend (&n_resp, (char *)n_hex) ||
-		!encoding_strappend (&e_resp, (char *)e_hex)
-	) {
-		error = GPG_ERR_ENOMEM;
-		goto cleanup;
-	}
-
-	if (
-		(error = assuan_write_status(
-			ctx,
-			"KEY-DATA",
-			n_resp
-		)) != GPG_ERR_NO_ERROR
-	) {
-		goto cleanup;
-	}
-
-	if (
-		(error = assuan_write_status(
-			ctx,
-			"KEY-DATA",
-			e_resp
-		)) != GPG_ERR_NO_ERROR
-	) {
-		goto cleanup;
+	gcry_mpi_t mpis[] = {params->a, params->b, params->c};
+	for (i = 0; i < count; ++i) {
+		if (resp[i] == NULL) {
+			error = GPG_ERR_ENOMEM;
+			goto cleanup;
+		}
+		if (
+			gcry_mpi_aprint (
+			    GCRYMPI_FMT_HEX,
+				&hex[i],
+				NULL,
+				mpis[i]
+		    )
+		) {
+			error = GPG_ERR_BAD_KEY;
+			goto cleanup;
+		}
+		if (!encoding_strappend (&resp[i], (char *) hex[i])) {
+			error = GPG_ERR_ENOMEM;
+			goto cleanup;
+		}
+		if (
+			(error = assuan_write_status(
+			    ctx,
+				"KEY-DATA",
+				resp[i]
+			)) != GPG_ERR_NO_ERROR
+		) {
+			goto cleanup;
+		}
 	}
 
 	error = GPG_ERR_NO_ERROR;
 
 cleanup:
 
-	if (n_mpi != NULL) {
-		gcry_mpi_release (n_mpi);
-		n_mpi = NULL;
-	}
-
-	if (e_mpi != NULL) {
-		gcry_mpi_release (e_mpi);
-		e_mpi = NULL;
-	}
-
-	if (n_hex != NULL) {
-		gcry_free (n_hex);
-		n_hex = NULL;
-	}
+	keyutil_params_free (params);
 
-	if (e_hex != NULL) {
-		gcry_free (e_hex);
-		e_hex = NULL;
-	}
-
-	if (n_resp != NULL) {
-		free (n_resp);
-		n_resp = NULL;
+	for (i = 0; i < sizeof(hex)/sizeof(hex[0]); ++i) {
+		if (hex[i] != NULL) {
+			gcry_free (hex[i]);
+			hex[i] = NULL;
+		}
 	}
 
-	if (e_resp != NULL) {
-		free (e_resp);
-		e_resp = NULL;
+	for (i = 0; i < sizeof(resp)/sizeof(resp[0]); ++i) {
+		if (resp[i] != NULL) {
+			free (resp[i]);
+			resp[i] = NULL;
+		}
 	}
 
 	if (blob != NULL) {
@@ -1865,4 +2052,3 @@ cleanup:
 
 	return gpg_error (error);
 }
-
diff --git a/gnupg-pkcs11-scd/common.c b/gnupg-pkcs11-scd/common.c
index 41f4c6a..918dbe2 100644
--- a/gnupg-pkcs11-scd/common.c
+++ b/gnupg-pkcs11-scd/common.c
@@ -118,3 +118,32 @@ common_map_pkcs11_error (int rv) {
 	return error;
 }
 
+void
+reverse_buffer (unsigned char *buffer, unsigned int length)
+{
+  unsigned int tmp, i;
+
+  for (i = 0; i < length/2; i++) {
+      tmp = buffer[i];
+      buffer[i] = buffer[length - 1 -i];
+      buffer[length - 1 - i] = tmp;
+  }
+}
+
+void
+reverse_hex (unsigned char *hex)
+{
+  unsigned char tmp_l, tmp_h;
+  unsigned int i, length;
+
+  length = strlen (hex);
+
+  for (i = 0; i < length/2 && (i + 1) < length; i += 2) {
+      tmp_h = hex[i];
+	  tmp_l = hex[i + 1];
+      hex[i] = hex[length - 2 - i];
+	  hex[i+1] = hex[length - 1 - i];
+      hex[length - 2 - i] = tmp_h;
+      hex[length - 1 - i] = tmp_l;
+  }
+}
diff --git a/gnupg-pkcs11-scd/common.h b/gnupg-pkcs11-scd/common.h
index 6ab37db..2c30598 100644
--- a/gnupg-pkcs11-scd/common.h
+++ b/gnupg-pkcs11-scd/common.h
@@ -84,4 +84,9 @@ common_log (
 	...
 );
 
+void
+reverse_buffer (unsigned char *buffer, unsigned int length);
+void
+reverse_hex (unsigned char *hex);
+
 #endif
diff --git a/gnupg-pkcs11-scd/gnupg-pkcs11-scd.1 b/gnupg-pkcs11-scd/gnupg-pkcs11-scd.1
index bf68366..b37a4b7 100644
--- a/gnupg-pkcs11-scd/gnupg-pkcs11-scd.1
+++ b/gnupg-pkcs11-scd/gnupg-pkcs11-scd.1
@@ -266,7 +266,7 @@ Typical steps to set up a card for gpg-2.0 usage:
 .It
 Acquire key ids:
 .Dl gpg-agent --server gpg-connect-agent
-Enter "SCD LEARN" and look for "KEY-FRIEDNLY" responses, the first field is the hash, the second
+Enter "SCD LEARN" and look for "KEY-FRIENDLY" responses, the first field is the hash, the second
 is the subject name.
 .It
 Instruct GnuPG to discover all useful information of card:
@@ -303,7 +303,7 @@ Refresh local key store:
 .It
 Acquire key ids:
 .Dl gpg-agent --server gpg-connect-agent
-Enter "SCD LEARN" and look for "KEY-FRIEDNLY" responses, the first field is the keygrip, the second
+Enter "SCD LEARN" and look for "KEY-FRIENDLY" responses, the first field is the keygrip, the second
 is the subject name.
 .It
 Create master key based on existing key using:
diff --git a/gnupg-pkcs11-scd/keyutil.c b/gnupg-pkcs11-scd/keyutil.c
index 1c99d2a..59d9e9e 100644
--- a/gnupg-pkcs11-scd/keyutil.c
+++ b/gnupg-pkcs11-scd/keyutil.c
@@ -62,29 +62,30 @@ void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM
 #endif
 
 gpg_err_code_t
-keyutil_get_cert_mpi (
+keyutil_get_cert_params (
 	unsigned char *der,
 	size_t len,
-	gcry_mpi_t *p_n_mpi,
-	gcry_mpi_t *p_e_mpi
+	cert_params_t **params
 ) {
 	gpg_err_code_t error = GPG_ERR_GENERAL;
-	gcry_mpi_t n_mpi = NULL;
-	gcry_mpi_t e_mpi = NULL;
 #if defined(ENABLE_GNUTLS)
 	gnutls_x509_crt_t cert = NULL;
 	gnutls_datum_t datum = {der, len};
 	gnutls_datum_t m = {NULL, 0}, e = {NULL, 0};
+	*params = gnutls_malloc (sizeof (**params));
 #elif defined(ENABLE_OPENSSL)
 	X509 *x509 = NULL;
 	EVP_PKEY *pubkey = NULL;
 	RSA *rsa = NULL;
-	const BIGNUM *n, *e;
-	char *n_hex = NULL, *e_hex = NULL;
+	EC_KEY* ec_key = NULL; int ec_key_ref = 0;
+	const BIGNUM *a = NULL, *b = NULL, *c = NULL;
+	char *a_hex = NULL, *b_hex = NULL, *c_hex = NULL;
+	*params = OPENSSL_malloc (sizeof (**params));
 #endif
 
-	*p_n_mpi = NULL;
-	*p_e_mpi = NULL;
+	(*params)->a = NULL;
+	(*params)->b = NULL;
+	(*params)->c = NULL;
 
 #if defined(ENABLE_GNUTLS)
 	if (gnutls_x509_crt_init (&cert) != GNUTLS_E_SUCCESS) {
@@ -106,8 +107,8 @@ keyutil_get_cert_mpi (
 	}
 
 	if (
-		gcry_mpi_scan(&n_mpi, GCRYMPI_FMT_USG, m.data, m.size, NULL) ||
-		gcry_mpi_scan(&e_mpi, GCRYMPI_FMT_USG, e.data, e.size, NULL)
+		gcry_mpi_scan(&(*params)->a, GCRYMPI_FMT_USG, m.data, m.size, NULL) ||
+		gcry_mpi_scan(&(*params)->b, GCRYMPI_FMT_USG, e.data, e.size, NULL)
 	) {
 		error = GPG_ERR_BAD_KEY;
 		goto cleanup;
@@ -122,51 +123,99 @@ keyutil_get_cert_mpi (
 		error = GPG_ERR_BAD_CERT;
 		goto cleanup;
 	}
- 
-	if ((rsa = EVP_PKEY_get1_RSA(pubkey)) == NULL) {
-		error = GPG_ERR_WRONG_PUBKEY_ALGO;
-		goto cleanup;
-	}
-
-	RSA_get0_key(rsa, &n, &e, NULL);
 
-	n_hex = BN_bn2hex (n);
-	e_hex = BN_bn2hex (e);
-
-	if(n_hex == NULL || e_hex == NULL) {
-		error = GPG_ERR_BAD_KEY;
-		goto cleanup;
+	int pubkey_type = EVP_PKEY_id(pubkey);
+	
+	switch (pubkey_type) {
+	case EVP_PKEY_RSA:
+		(*params)->key_type = KEY_RSA;
+		(*params)->key_subtype = KEY_RSA_RSA;
+		if ((rsa = EVP_PKEY_get1_RSA(pubkey)) == NULL) {
+			error = GPG_ERR_WRONG_PUBKEY_ALGO;
+			goto cleanup;
+		}
+		RSA_get0_key(rsa, &a, &b, NULL);
+		if ( a ) a_hex = BN_bn2hex (a);
+		if ( b ) b_hex = BN_bn2hex (b);
+ 		if (a_hex == NULL || b_hex == NULL) {
+			error = GPG_ERR_BAD_CERT;
+			goto cleanup;
+		}
+		break;
+	case NID_id_GostR3410_2001:
+		(*params)->key_subtype = KEY_ECC_GOST2001;
+        goto gostkey;
+    case NID_id_GostR3410_2012_256:
+        (*params)->key_subtype = KEY_ECC_GOST2012_256;
+        goto gostkey;
+    case NID_id_GostR3410_2012_512:
+        (*params)->key_subtype = KEY_ECC_GOST2012_512;
+        goto gostkey;
+    gostkey:
+        ec_key = (EC_KEY *) EVP_PKEY_get0(pubkey);
+        goto ecc;
+    ecc:
+	case EVP_PKEY_EC:
+		(*params)->key_type = KEY_ECC;
+		if (!ec_key) {
+			ec_key = EVP_PKEY_get1_EC_KEY(pubkey);
+			ec_key_ref = 1;
+		}
+		int ret = 0;
+		if (ec_key) {
+			const EC_GROUP *group = EC_KEY_get0_group(ec_key);
+			const EC_POINT *pub_key = EC_KEY_get0_public_key(ec_key);
+
+			if (group && pub_key) {
+				(*params)->nid = EC_GROUP_get_curve_name(group);
+				BIGNUM *X = BN_new(), *Y = BN_new(), *Z = BN_new();
+				if (X && Y && Z) {
+					ret = EC_POINT_get_Jprojective_coordinates_GFp(group,
+																	   pub_key,
+																	   X, Y, Z,
+																	   NULL);
+					if (ret) {
+						a_hex = BN_bn2hex (X);
+						b_hex = BN_bn2hex (Y);
+						c_hex = BN_bn2hex (Z);
+					}
+					BN_free(X); BN_free(Y); BN_free(Z);
+				}
+			}
+			if (ec_key_ref)
+				EC_KEY_free(ec_key);
+		}
+
+		if (!ret) {
+			error = GPG_ERR_BAD_CERT;
+			goto cleanup;
+		}
+ 		if (a_hex == NULL || b_hex == NULL || c_hex == NULL) {
+			error = GPG_ERR_BAD_CERT;
+			goto cleanup;
+		}
+		break;
+	/*default:
+		error = GPG_ERR_WRONG_PUBKEY_ALGO;
+		goto cleanup;*/
 	}
  
 	if (
-		gcry_mpi_scan (&n_mpi, GCRYMPI_FMT_HEX, n_hex, 0, NULL) ||
-		gcry_mpi_scan (&e_mpi, GCRYMPI_FMT_HEX, e_hex, 0, NULL)
+		a_hex && gcry_mpi_scan (&(*params)->a, GCRYMPI_FMT_HEX, a_hex, 0, NULL) ||
+		b_hex && gcry_mpi_scan (&(*params)->b, GCRYMPI_FMT_HEX, b_hex, 0, NULL) ||
+		c_hex && gcry_mpi_scan (&(*params)->c, GCRYMPI_FMT_HEX, c_hex, 0, NULL)
 	) {
-		error = GPG_ERR_BAD_KEY;
+		error = GPG_ERR_BAD_KEY;		
 		goto cleanup;
 	}
 #else
 #error Invalid configuration.
 #endif
 
-	*p_n_mpi = n_mpi;
-	n_mpi = NULL;
-	*p_e_mpi = e_mpi;
-	e_mpi = NULL;
 	error = GPG_ERR_NO_ERROR;
 
 cleanup:
 
-	if (n_mpi != NULL) {
-		gcry_mpi_release (n_mpi);
-		n_mpi = NULL;
-	}
-
-	if (e_mpi != NULL) {
-		gcry_mpi_release (e_mpi);
-		e_mpi = NULL;
-	}
-
 #if defined(ENABLE_GNUTLS)
 
 	if (m.data != NULL) {
@@ -200,23 +249,55 @@ cleanup:
 		RSA_free(rsa);
 		rsa = NULL;
 	}
-
-	if (n_hex != NULL) {
-		OPENSSL_free (n_hex);
-		n_hex = NULL;
+	
+	if (a_hex != NULL) {
+		OPENSSL_free (a_hex);
+		a_hex = NULL;
 	}
-
-	if (e_hex != NULL) {
-		OPENSSL_free (e_hex);
-		e_hex = NULL;
+	if (b_hex != NULL) {
+		OPENSSL_free (b_hex);
+		b_hex = NULL;
+	}
+	if (c_hex != NULL) {
+		OPENSSL_free (c_hex);
+		c_hex = NULL;
 	}
 
 #else
 #error Invalid configuration.
 #endif
 
+	if (error != GPG_ERR_NO_ERROR) {
+		keyutil_params_free (*params);
+		*params = NULL;
+	}
+	
 	return error;
 }
+
+void
+keyutil_params_free (cert_params_t *params) {
+	if (params) {
+		if (params->a) {
+			gcry_mpi_release (params->a);
+			params->a = NULL;
+		}
+		if (params->b) {
+			gcry_mpi_release (params->b);
+			params->b = NULL;
+		}
+		if (params->c) {
+			gcry_mpi_release (params->c);
+			params->c = NULL;
+		}
+#if defined(ENABLE_GNUTLS)
+		gnutls_free (params);
+#elif defined(ENABLE_OPENSSL)
+		OPENSSL_free (params);
+#endif
+	}
+}
+
 /**
    Convert X.509 RSA public key into gcrypt internal sexp form. Only RSA
    public keys are accepted at the moment. The resul is stored in *sexp,
@@ -230,50 +311,164 @@ keyutil_get_cert_sexp (
 	gcry_sexp_t *p_sexp
 ) {
 	gpg_err_code_t error = GPG_ERR_GENERAL;
-	gcry_mpi_t n_mpi = NULL;
-	gcry_mpi_t e_mpi = NULL;
+	cert_params_t *params = NULL;
+	const char *curve_name = NULL;
+	gcry_ctx_t r_ctx = NULL;
+	gcry_sexp_t keyparam = NULL;
 	gcry_sexp_t sexp = NULL;
 
 	if (
-		(error = keyutil_get_cert_mpi (
-			der,
-			len,
-			&n_mpi,
-			&e_mpi
-		)) != GPG_ERR_NO_ERROR
+		(error = keyutil_get_cert_params (der, len, &params))
+		!= GPG_ERR_NO_ERROR
 	) {
 		goto cleanup;
 	}
 
-	if (
-		gcry_sexp_build (
-			&sexp,
-			NULL,
-			"(public-key (rsa (n %m) (e %m)))",
-			n_mpi,
-			e_mpi
-		)
-	) {
-		error = GPG_ERR_BAD_KEY;
+	switch (params->key_type) {
+	case KEY_RSA:
+		if (
+			gcry_sexp_build (
+			    &sexp,
+				NULL,
+				"(public-key (rsa (n %m) (e %m)))",
+				params->a,
+				params->b
+			)
+		) {
+			error = GPG_ERR_BAD_KEY;
+			goto cleanup;
+		}
+		break;
+	case KEY_ECC:
+		switch (params->nid) {
+		case NID_id_GostR3410_2001_CryptoPro_A_ParamSet:
+            switch (params->key_subtype) {
+            case KEY_ECC_GOST2001:
+                curve_name = "GOST2001-CryptoPro-A";
+                break;
+            case KEY_ECC_GOST2012_256:
+                curve_name = "GOST2012-256-B";
+                break;
+            default:
+                error = GPG_ERR_BAD_CERT;
+                goto cleanup;
+            }
+			break;
+		case NID_id_GostR3410_2001_CryptoPro_B_ParamSet:
+            switch (params->key_subtype) {
+            case KEY_ECC_GOST2001:
+                curve_name = "GOST2001-CryptoPro-B";
+                break;
+            case KEY_ECC_GOST2012_256:
+                curve_name = "GOST2012-256-C";
+                break;
+            default:
+                error = GPG_ERR_BAD_CERT;
+                goto cleanup;
+            }
+			break;
+		case NID_id_GostR3410_2001_CryptoPro_C_ParamSet:
+            switch (params->key_subtype) {
+            case KEY_ECC_GOST2001:
+                curve_name = "GOST2001-CryptoPro-C";
+                break;
+            case KEY_ECC_GOST2012_256:
+                curve_name = "GOST2012-256-D";
+                break;
+            default:
+                error = GPG_ERR_BAD_CERT;
+                goto cleanup;
+            }
+			break;
+		case NID_id_GostR3410_2001_CryptoPro_XchA_ParamSet:
+			curve_name = "GOST2001-CryptoPro-XchA";
+			break;
+		case NID_id_GostR3410_2001_CryptoPro_XchB_ParamSet:
+			curve_name = "GOST2001-CryptoPro-XchB";
+			break;
+        case NID_id_tc26_gost_3410_2012_256_paramSetA:
+            curve_name = "GOST2012-256-A";
+        case NID_id_tc26_gost_3410_2012_256_paramSetB:
+            curve_name = "GOST2012-256-B";
+        case NID_id_tc26_gost_3410_2012_256_paramSetC:
+            curve_name = "GOST2012-256-C";
+        case NID_id_tc26_gost_3410_2012_256_paramSetD:
+            curve_name = "GOST2012-256-D";
+        case NID_id_tc26_gost_3410_2012_512_paramSetA:
+            curve_name = "GOST2012-512-A";
+            break;
+        case NID_id_tc26_gost_3410_2012_512_paramSetB:
+            curve_name = "GOST2012-512-B";
+            break;
+		default:
+			error = GPG_ERR_BAD_CERT;
+			goto cleanup;
+		}
+		
+		if (
+			gcry_sexp_build (
+			    &keyparam,
+				NULL,
+				"(public-key\n"
+				" (ecc\n"
+				"  (curve %s)\n"
+				"  (q.x %m)\n"
+				"  (q.y %m)\n"
+				"  (q.z %m)))\n",
+				curve_name,
+				params->a,
+				params->b,
+				params->c
+			)
+		) {
+			error = GPG_ERR_BAD_KEY;
+			goto cleanup;
+		}
+
+		error = gcry_mpi_ec_new (&r_ctx,
+								 keyparam,
+								 curve_name);
+		if (error != GPG_ERR_NO_ERROR) {
+			goto cleanup;
+		}
+
+		if (
+			gcry_sexp_build (
+			    &sexp,
+				NULL,
+				"(public-key\n"
+				" (ecc\n"
+				"  (curve %s)\n"
+				"  (q %m)))\n",
+				curve_name,
+				gcry_mpi_ec_get_mpi ("q", r_ctx, 1)
+			)
+		) {
+			error = GPG_ERR_BAD_KEY;
+			goto cleanup;
+		}		
+		break;
+	default:
+		error = GPG_ERR_BAD_CERT;
 		goto cleanup;
 	}
-
+	
 	*p_sexp = sexp;
 	sexp = NULL;
 	error = GPG_ERR_NO_ERROR;
 
 cleanup:
 
-	if (n_mpi != NULL) {
-		gcry_mpi_release (n_mpi);
-		n_mpi = NULL;
-	}
+	keyutil_params_free (params);
 
-	if (e_mpi != NULL) {
-		gcry_mpi_release (e_mpi);
-		e_mpi = NULL;
+	if (keyparam != NULL) {
+		gcry_sexp_release (keyparam);
+		keyparam = NULL;
+	}
+	if (r_ctx != NULL) {
+		gcry_ctx_release (r_ctx);
+		r_ctx = NULL;
 	}
-
 	if (sexp != NULL) {
 		gcry_sexp_release (sexp);
 		sexp = NULL;
diff --git a/gnupg-pkcs11-scd/keyutil.h b/gnupg-pkcs11-scd/keyutil.h
index c426410..7850904 100644
--- a/gnupg-pkcs11-scd/keyutil.h
+++ b/gnupg-pkcs11-scd/keyutil.h
@@ -31,14 +31,34 @@
 #ifndef __KEYUTIL_H
 #define __KEYUTIL_H
 
+typedef enum {KEY_RSA, KEY_ECC} key_type_t;
+typedef enum {
+    KEY_UNKNOWN,
+    KEY_RSA_RSA,
+    KEY_ECC_GOST2001,
+    KEY_ECC_GOST2012_256,
+    KEY_ECC_GOST2012_512
+} key_subtype_t;
+
+typedef struct {
+	key_type_t key_type;
+	key_subtype_t key_subtype;
+	int nid;
+	gcry_mpi_t a;
+	gcry_mpi_t b;
+	gcry_mpi_t c;
+} cert_params_t;
+
 gpg_err_code_t
-keyutil_get_cert_mpi (
+keyutil_get_cert_params (
 	unsigned char *der,
 	size_t len,
-	gcry_mpi_t *p_n_mpi,
-	gcry_mpi_t *p_e_mpi
+	cert_params_t **params
 );
 
+void
+keyutil_params_free (cert_params_t *params);
+
 gpg_err_code_t
 keyutil_get_cert_sexp (
 	unsigned char *der,
@@ -46,6 +66,7 @@ keyutil_get_cert_sexp (
 	gcry_sexp_t *p_sexp
 );
 
-char *keyutil_get_cert_hexgrip (gcry_sexp_t sexp);
+char *
+keyutil_get_cert_hexgrip (gcry_sexp_t sexp);
 
 #endif
diff --git a/gnupg-pkcs11-scd/scdaemon.c b/gnupg-pkcs11-scd/scdaemon.c
index d646584..a7fb5c1 100644
--- a/gnupg-pkcs11-scd/scdaemon.c
+++ b/gnupg-pkcs11-scd/scdaemon.c
@@ -56,6 +56,10 @@
 #endif
 #endif
 
+#if defined(ENABLE_OPENSSL)
+#include <openssl/conf.h>
+#endif
+
 #if defined(USE_GNUTLS)
 #include <gnutls/gnutls.h>
 #endif
@@ -787,6 +791,36 @@ static char *get_home_dir (void) {
 	return home_dir;
 }
 
+static void
+my_gcry_logger (void * _global, int level, const char *fmt, va_list arg_ptr)
+{
+  global_t *global = (global_t *) _global;
+
+  /* Map the log levels.  */
+  switch ( level ) {
+  case GCRY_LOG_CONT: 
+  case GCRY_LOG_INFO:
+	  if ( !global->config.verbose ) return;
+	  break;
+  case GCRY_LOG_WARN:
+  case GCRY_LOG_ERROR:
+  case GCRY_LOG_FATAL:
+  case GCRY_LOG_BUG:
+	  break;
+  case GCRY_LOG_DEBUG:
+	  if ( !global->config.debug ) return;
+	  break;
+  }
+
+  FILE *log_stream = common_get_log_stream ();
+  if ( log_stream ) {
+	  vfprintf (log_stream, fmt, arg_ptr);
+	  fflush (log_stream);
+  }
+
+  if ( level == GCRY_LOG_FATAL ) exit (1);
+}
+
 int main (int argc, char *argv[])
 {
 	enum {
@@ -1007,8 +1041,14 @@ int main (int argc, char *argv[])
 		);
 	}
 
+#if defined(ENABLE_OPENSSL)
+	OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
+#endif
+
+	gcry_set_log_handler (my_gcry_logger, &global);
+
 	if (!gcry_check_version (GCRYPT_VERSION)) {
-		common_log (LOG_FATAL, "Cannot initialize libcrypt");
+		common_log (LOG_FATAL, "Cannot initialize libgcrypt");
 	}
 	gcry_control (GCRYCTL_DISABLE_SECMEM, 0);
 	gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
 
дизайн и разработка: Vladimir Lettiev aka crux © 2004-2005, Andrew Avramenko aka liks © 2007-2008
текущий майнтейнер: Michael Shigorin