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, + ¶ms + )) != 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 + ¶ms )) != 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, ¶ms)) + != 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 +#endif + #if defined(USE_GNUTLS) #include #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);