From 6b8051aeeb74efc28eadb344783ab0e79963198a Mon Sep 17 00:00:00 2001 From: Dmitry Eremin-Solenikov Date: Mon, 18 Sep 2017 12:54:12 +0300 Subject: Support importing/exporting X.509 GOST public keys Signed-off-by: Dmitry Eremin-Solenikov --- doc/Makefile.am | 6 + doc/manpages/Makefile.am | 3 + lib/includes/gnutls/abstract.h | 15 +++ lib/includes/gnutls/x509.h | 5 + lib/libgnutls.map | 3 + lib/pubkey.c | 169 ++++++++++++++++++++++++++++ lib/x509/key_decode.c | 150 +++++++++++++++++++++++++ lib/x509/key_encode.c | 171 +++++++++++++++++++++++++++++ lib/x509/output.c | 63 +++++++++++ lib/x509/x509.c | 53 +++++++++ lib/x509/x509_int.h | 4 + symbols.last | 3 + tests/cert-tests/Makefile.am | 9 +- tests/cert-tests/data/gost-cert-nogost.pem | 45 ++++++++ tests/cert-tests/data/gost-cert.pem | 16 +++ tests/cert-tests/data/gost94-cert.pem | 33 ++++++ tests/cert-tests/pem-decoding | 27 ++++- 17 files changed, 772 insertions(+), 3 deletions(-) create mode 100644 tests/cert-tests/data/gost-cert-nogost.pem create mode 100644 tests/cert-tests/data/gost94-cert.pem diff --git a/doc/Makefile.am b/doc/Makefile.am index f56ee101a5..68a2702227 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1700,6 +1700,8 @@ FUNCS += functions/gnutls_pubkey_export_ecc_raw2 FUNCS += functions/gnutls_pubkey_export_ecc_raw2.short FUNCS += functions/gnutls_pubkey_export_ecc_x962 FUNCS += functions/gnutls_pubkey_export_ecc_x962.short +FUNCS += functions/gnutls_pubkey_export_gost_raw2 +FUNCS += functions/gnutls_pubkey_export_gost_raw2.short FUNCS += functions/gnutls_pubkey_export_rsa_raw FUNCS += functions/gnutls_pubkey_export_rsa_raw.short FUNCS += functions/gnutls_pubkey_export_rsa_raw2 @@ -1724,6 +1726,8 @@ FUNCS += functions/gnutls_pubkey_import_ecc_raw FUNCS += functions/gnutls_pubkey_import_ecc_raw.short FUNCS += functions/gnutls_pubkey_import_ecc_x962 FUNCS += functions/gnutls_pubkey_import_ecc_x962.short +FUNCS += functions/gnutls_pubkey_import_gost_raw +FUNCS += functions/gnutls_pubkey_import_gost_raw.short FUNCS += functions/gnutls_pubkey_import_openpgp FUNCS += functions/gnutls_pubkey_import_openpgp.short FUNCS += functions/gnutls_pubkey_import_openpgp_raw @@ -2386,6 +2390,8 @@ FUNCS += functions/gnutls_x509_crt_get_pk_dsa_raw FUNCS += functions/gnutls_x509_crt_get_pk_dsa_raw.short FUNCS += functions/gnutls_x509_crt_get_pk_ecc_raw FUNCS += functions/gnutls_x509_crt_get_pk_ecc_raw.short +FUNCS += functions/gnutls_x509_crt_get_pk_gost_raw +FUNCS += functions/gnutls_x509_crt_get_pk_gost_raw.short FUNCS += functions/gnutls_x509_crt_get_pk_oid FUNCS += functions/gnutls_x509_crt_get_pk_oid.short FUNCS += functions/gnutls_x509_crt_get_pk_rsa_raw diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am index 5679518a29..c10719f4f6 100644 --- a/doc/manpages/Makefile.am +++ b/doc/manpages/Makefile.am @@ -645,6 +645,7 @@ APIMANS += gnutls_pubkey_export_dsa_raw2.3 APIMANS += gnutls_pubkey_export_ecc_raw.3 APIMANS += gnutls_pubkey_export_ecc_raw2.3 APIMANS += gnutls_pubkey_export_ecc_x962.3 +APIMANS += gnutls_pubkey_export_gost_raw2.3 APIMANS += gnutls_pubkey_export_rsa_raw.3 APIMANS += gnutls_pubkey_export_rsa_raw2.3 APIMANS += gnutls_pubkey_get_key_id.3 @@ -657,6 +658,7 @@ APIMANS += gnutls_pubkey_import.3 APIMANS += gnutls_pubkey_import_dsa_raw.3 APIMANS += gnutls_pubkey_import_ecc_raw.3 APIMANS += gnutls_pubkey_import_ecc_x962.3 +APIMANS += gnutls_pubkey_import_gost_raw.3 APIMANS += gnutls_pubkey_import_openpgp.3 APIMANS += gnutls_pubkey_import_openpgp_raw.3 APIMANS += gnutls_pubkey_import_pkcs11.3 @@ -988,6 +990,7 @@ APIMANS += gnutls_x509_crt_get_name_constraints.3 APIMANS += gnutls_x509_crt_get_pk_algorithm.3 APIMANS += gnutls_x509_crt_get_pk_dsa_raw.3 APIMANS += gnutls_x509_crt_get_pk_ecc_raw.3 +APIMANS += gnutls_x509_crt_get_pk_gost_raw.3 APIMANS += gnutls_x509_crt_get_pk_oid.3 APIMANS += gnutls_x509_crt_get_pk_rsa_raw.3 APIMANS += gnutls_x509_crt_get_policy.3 diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index d3cd91b93c..bd947b7833 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -216,6 +216,13 @@ int gnutls_pubkey_export_ecc_raw2(gnutls_pubkey_t key, gnutls_datum_t * x, gnutls_datum_t * y, unsigned flags); +int gnutls_pubkey_export_gost_raw2(gnutls_pubkey_t key, + gnutls_ecc_curve_t * curve, + gnutls_digest_algorithm_t * digest, + gnutls_gost_paramset_t * paramset, + gnutls_datum_t * x, gnutls_datum_t * y, + unsigned int flags); + #define gnutls_pubkey_get_pk_ecc_raw gnutls_pubkey_export_ecc_raw int gnutls_pubkey_export_ecc_raw(gnutls_pubkey_t key, gnutls_ecc_curve_t * curve, @@ -276,6 +283,14 @@ gnutls_pubkey_import_ecc_raw(gnutls_pubkey_t key, const gnutls_datum_t * x, const gnutls_datum_t * y); +int +gnutls_pubkey_import_gost_raw(gnutls_pubkey_t key, + gnutls_ecc_curve_t curve, + gnutls_digest_algorithm_t digest, + gnutls_gost_paramset_t paramset, + const gnutls_datum_t * x, + const gnutls_datum_t * y); + int gnutls_pubkey_encrypt_data(gnutls_pubkey_t key, unsigned int flags, diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index fef901a101..ff6fa52822 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -455,6 +455,11 @@ int gnutls_x509_crt_get_pk_ecc_raw(gnutls_x509_crt_t crt, gnutls_ecc_curve_t * curve, gnutls_datum_t * x, gnutls_datum_t * y); +int gnutls_x509_crt_get_pk_gost_raw(gnutls_x509_crt_t crt, + gnutls_ecc_curve_t * curve, + gnutls_digest_algorithm_t * digest, + gnutls_gost_paramset_t *paramset, + gnutls_datum_t * x, gnutls_datum_t * y); int gnutls_x509_crt_get_subject_alt_name(gnutls_x509_crt_t cert, unsigned int seq, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 6d81cf8b9c..b1b6630ed9 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1226,6 +1226,9 @@ GNUTLS_3_6_3 gnutls_oid_to_gost_paramset; gnutls_decode_gost_rs_value; gnutls_encode_gost_rs_value; + gnutls_pubkey_export_gost_raw2; + gnutls_pubkey_import_gost_raw; + gnutls_x509_crt_get_pk_gost_raw; } GNUTLS_3_6_2; GNUTLS_FIPS140_3_4 { diff --git a/lib/pubkey.c b/lib/pubkey.c index d6d374b786..7c9b6da5f8 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -61,6 +61,9 @@ unsigned pubkey_to_bits(const gnutls_pk_params_st * params) return _gnutls_mpi_get_nbits(params->params[DSA_P]); case GNUTLS_PK_ECDSA: case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: return gnutls_ecc_curve_get_size(params->curve) * 8; default: return 0; @@ -311,6 +314,16 @@ gnutls_pubkey_get_preferred_hash_algorithm(gnutls_pubkey_t key, if (hash) *hash = GNUTLS_DIG_SHA512; + ret = 0; + break; + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + if (hash) + *hash = _gnutls_gost_digest(key->params.algo); + if (mand) + *mand = 1; + ret = 0; break; case GNUTLS_PK_RSA_PSS: @@ -958,6 +971,82 @@ int gnutls_pubkey_export_ecc_x962(gnutls_pubkey_t key, return ret; } +/** + * gnutls_pubkey_export_gost_raw2: + * @key: Holds the public key + * @curve: will hold the curve (may be %NULL) + * @digest: will hold the curve (may be %NULL) + * @paramset: will hold the parameters id (may be %NULL) + * @x: will hold x (may be %NULL) + * @y: will hold y (may be %NULL) + * @flags: flags from %gnutls_abstract_export_flags_t + * + * This function will export the GOST public key's parameters found in + * the given key. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.6.3 + **/ +int +gnutls_pubkey_export_gost_raw2(gnutls_pubkey_t key, + gnutls_ecc_curve_t * curve, + gnutls_digest_algorithm_t * digest, + gnutls_gost_paramset_t * paramset, + gnutls_datum_t * x, gnutls_datum_t * y, + unsigned int flags) +{ + int ret; + + mpi_dprint_func dprint = _gnutls_mpi_dprint_lz; + + if (flags & GNUTLS_EXPORT_FLAG_NO_LZ) + dprint = _gnutls_mpi_dprint; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (key->params.algo != GNUTLS_PK_GOST_01 && + key->params.algo != GNUTLS_PK_GOST_12_256 && + key->params.algo != GNUTLS_PK_GOST_12_512) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (curve) + *curve = key->params.curve; + + if (digest) + *digest = _gnutls_gost_digest(key->params.algo); + + if (paramset) + *paramset = key->params.gost_params; + + /* X */ + if (x) { + ret = dprint(key->params.params[GOST_X], x); + if (ret < 0) { + gnutls_assert(); + return ret; + } + } + + /* Y */ + if (y) { + ret = dprint(key->params.params[GOST_Y], y); + if (ret < 0) { + gnutls_assert(); + _gnutls_free_datum(x); + return ret; + } + } + + return 0; +} + /** * gnutls_pubkey_import: * @key: The public key. @@ -1434,6 +1523,80 @@ gnutls_pubkey_import_ecc_x962(gnutls_pubkey_t key, return ret; } +/** + * gnutls_pubkey_import_gost_raw: + * @key: The structure to store the parsed key + * @curve: holds the curve + * @digest: holds the digest + * @paramset: holds the parameters id + * @x: holds the x + * @y: holds the y + * + * This function will convert the given GOST public key's parameters to a + * #gnutls_pubkey_t. The output will be stored in @key. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.3 + **/ +int +gnutls_pubkey_import_gost_raw(gnutls_pubkey_t key, + gnutls_ecc_curve_t curve, + gnutls_digest_algorithm_t digest, + gnutls_gost_paramset_t paramset, + const gnutls_datum_t * x, + const gnutls_datum_t * y) +{ + int ret; + gnutls_pk_algorithm_t pk_algo; + + if (key == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + pk_algo = _gnutls_digest_gost(digest); + if (pk_algo == GNUTLS_PK_UNKNOWN) + return GNUTLS_E_ILLEGAL_PARAMETER; + + if (paramset < 0) { + if (pk_algo == GNUTLS_PK_GOST_01) + paramset = GNUTLS_GOST_PARAMSET_CP_A; + else + paramset = GNUTLS_GOST_PARAMSET_TC26_Z; + } + + gnutls_pk_params_release(&key->params); + gnutls_pk_params_init(&key->params); + + key->params.curve = curve; + key->params.gost_params = paramset; + + if (_gnutls_mpi_init_scan_nz + (&key->params.params[GOST_X], x->data, x->size)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + + if (_gnutls_mpi_init_scan_nz + (&key->params.params[GOST_Y], y->data, y->size)) { + gnutls_assert(); + ret = GNUTLS_E_MPI_SCAN_FAILED; + goto cleanup; + } + key->params.params_nr++; + key->params.algo = pk_algo; + + return 0; + + cleanup: + gnutls_pk_params_release(&key->params); + return ret; +} + /** * gnutls_pubkey_import_dsa_raw: * @key: The structure to store the parsed key @@ -1993,6 +2156,9 @@ pubkey_verify_hashed_data(const gnutls_sign_entry_st *se, break; case GNUTLS_PK_ECDSA: + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: case GNUTLS_PK_DSA: if (dsa_verify_hashed_data (se->pk, me, hash, signature, params, sign_params) != 0) { @@ -2057,6 +2223,9 @@ pubkey_verify_data(const gnutls_sign_entry_st *se, case GNUTLS_PK_EC: case GNUTLS_PK_DSA: + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: if (dsa_verify_data (se->pk, me, data, signature, params, sign_params) != 0) { gnutls_assert(); diff --git a/lib/x509/key_decode.c b/lib/x509/key_decode.c index a1bf82688a..b1df8aaab3 100644 --- a/lib/x509/key_decode.c +++ b/lib/x509/key_decode.c @@ -39,6 +39,8 @@ static int _gnutls_x509_read_ecc_pubkey(uint8_t * der, int dersize, gnutls_pk_params_st * params); static int _gnutls_x509_read_eddsa_pubkey(uint8_t * der, int dersize, gnutls_pk_params_st * params); +static int _gnutls_x509_read_gost_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params); static int _gnutls_x509_read_dsa_params(uint8_t * der, int dersize, @@ -117,6 +119,47 @@ int _gnutls_x509_read_eddsa_pubkey(uint8_t * der, int dersize, return _gnutls_set_datum(¶ms->raw_pub, der, dersize); } +/* Pubkey is a concatenation of X (in little endian) and Y (also LE) + * encoded into OCTET STRING. */ +static int +_gnutls_x509_read_gost_pubkey(uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + int ret; + int len; + bigint_t *x = ¶ms->params[GOST_X]; + bigint_t *y = ¶ms->params[GOST_Y]; + + /* Quick and dirty parsing of OCTET STRING of 0x40 or 0x80 bytes */ + if (dersize < 1 || der[0] != ASN1_TAG_OCTET_STRING) { + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + + der++; + dersize--; + + ret = asn1_get_length_der(der, dersize, &len); + if (ret <= 0 || ret % 2 != 0 || dersize != len + ret) { + return gnutls_assert_val(GNUTLS_E_PARSING_ERROR); + } + + der += len; + dersize -= len; + + /* read data */ + ret = _gnutls_mpi_init_scan_le(x, der, dersize / 2); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + ret = _gnutls_mpi_init_scan_le(y, der + dersize / 2, dersize / 2); + if (ret < 0) { + _gnutls_mpi_release(y); + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + + return 0; +} + /* reads p,q and g * from the certificate (subjectPublicKey BIT STRING). * params[0-2]. It does NOT set params_nr. @@ -387,6 +430,97 @@ _gnutls_x509_read_rsa_pss_params(uint8_t * der, int dersize, return result; } +/* reads the curve from the certificate. + * It does NOT set params_nr. + */ +int +_gnutls_x509_read_gost_params(uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + int ret; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + char oid[MAX_OID_SIZE]; + int oid_size; + gnutls_ecc_curve_t curve; + int param; + + if ((ret = asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.GOSTParameters", + &spk)) != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = _asn1_strict_der_decode(&spk, der, dersize, NULL); + + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + /* read the curve */ + oid_size = sizeof(oid); + ret = asn1_read_value(spk, "publicKeyParamSet", oid, &oid_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + curve = gnutls_oid_to_ecc_curve(oid); + if (curve == GNUTLS_ECC_CURVE_INVALID) { + _gnutls_debug_log("Curve %s is not supported\n", oid); + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + goto cleanup; + } + + /* Read the digest */ + oid_size = sizeof(oid); + ret = asn1_read_value(spk, "digestParamSet", oid, &oid_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + /* For now ignore the OID: we use pk OID instead */ + + if (!strcmp(oid, HASH_OID_GOST_R_3411_94_CRYPTOPRO_PARAMS)) + param = GNUTLS_GOST_PARAMSET_CP_A; + else + param = GNUTLS_GOST_PARAMSET_TC26_Z; + + oid_size = sizeof(oid); + ret = asn1_read_value(spk, "encryptionParamSet", oid, &oid_size); + if (ret != ASN1_SUCCESS && + ret != ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + if (ret != ASN1_ELEMENT_NOT_FOUND) + param = gnutls_oid_to_gost_paramset(oid); + + if (param < 0) { + gnutls_assert(); + ret = param; + goto cleanup; + } + + params->curve = curve; + params->gost_params = param; + ret = 0; + + cleanup: + + asn1_delete_structure(&spk); + + return ret; + +} + /* This function must be called after _gnutls_x509_read_params() */ int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der, @@ -423,6 +557,15 @@ int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der, case GNUTLS_PK_EDDSA_ED25519: ret = _gnutls_x509_read_eddsa_pubkey(der, dersize, params); break; + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + ret = _gnutls_x509_read_gost_pubkey(der, dersize, params); + if (ret >= 0) { + params->algo = algo; + params->params_nr = GOST_PUBLIC_PARAMS; + } + break; default: ret = gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); break; @@ -446,6 +589,10 @@ int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t algo, return _gnutls_x509_read_dsa_params(der, dersize, params); case GNUTLS_PK_EC: return _gnutls_x509_read_ecc_params(der, dersize, ¶ms->curve); + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + return _gnutls_x509_read_gost_params(der, dersize, params); default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } @@ -479,6 +626,9 @@ int _gnutls_x509_check_pubkey_params(gnutls_pk_params_st * params) case GNUTLS_PK_DSA: case GNUTLS_PK_ECDSA: case GNUTLS_PK_EDDSA_ED25519: + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: return 0; default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); diff --git a/lib/x509/key_encode.c b/lib/x509/key_encode.c index 0f12975d18..7663300826 100644 --- a/lib/x509/key_encode.c +++ b/lib/x509/key_encode.c @@ -39,6 +39,10 @@ static int _gnutls_x509_write_dsa_params(gnutls_pk_params_st * params, gnutls_datum_t * der); static int _gnutls_x509_write_dsa_pubkey(gnutls_pk_params_st * params, gnutls_datum_t * der); +static int _gnutls_x509_write_gost_params(gnutls_pk_params_st * params, + gnutls_datum_t * der); +static int _gnutls_x509_write_gost_pubkey(gnutls_pk_params_st * params, + gnutls_datum_t * der); /* * some x509 certificate functions that relate to MPI parameter @@ -156,6 +160,78 @@ _gnutls_x509_write_eddsa_pubkey(gnutls_pk_params_st * params, return 0; } +int +_gnutls_x509_write_gost_pubkey(gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + bigint_t x, y; + int numlen; + int byte_size, ret; + size_t size; + int pos; + + der->data = NULL; + der->size = 0; + + if (params->params_nr < GOST_PUBLIC_PARAMS) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + x = params->params[GOST_X]; + y = params->params[GOST_Y]; + numlen = gnutls_ecc_curve_get_size(params->curve); + + if (numlen == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + der->size = 1 + ASN1_MAX_LENGTH_SIZE + 2 * numlen; + + der->data = gnutls_malloc(der->size); + if (der->data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + memset(der->data, 0, der->size); + + der->data[0] = ASN1_TAG_OCTET_STRING; + asn1_length_der(2 * numlen, &der->data[1], &pos); + pos += 1; + + /* pad and store x */ + byte_size = (_gnutls_mpi_get_nbits(x) + 7) / 8; + if (numlen < byte_size) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + size = numlen; + ret = _gnutls_mpi_print_le(x, &der->data[pos], &size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + /* pad and store y */ + byte_size = (_gnutls_mpi_get_nbits(y) + 7) / 8; + if (numlen < byte_size) { + ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + goto cleanup; + } + + size = numlen; + ret = _gnutls_mpi_print_le(y, &der->data[pos + numlen], &size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + der->size = pos + 2 * numlen; + + return 0; + + cleanup: + _gnutls_free_datum(der); + return ret; +} + int _gnutls_x509_write_pubkey_params(gnutls_pk_params_st * params, gnutls_datum_t * der) @@ -180,6 +256,10 @@ _gnutls_x509_write_pubkey_params(gnutls_pk_params_st * params, der->size = 0; return 0; + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + return _gnutls_x509_write_gost_params(params, der); default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } @@ -199,6 +279,10 @@ _gnutls_x509_write_pubkey(gnutls_pk_params_st * params, return _gnutls_x509_write_ecc_pubkey(params, der); case GNUTLS_PK_EDDSA_ED25519: return _gnutls_x509_write_eddsa_pubkey(params, der); + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + return _gnutls_x509_write_gost_pubkey(params, der); default: return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); } @@ -439,6 +523,93 @@ _gnutls_x509_write_rsa_pss_params(gnutls_x509_spki_st *params, return result; } +static int +_gnutls_x509_write_gost_params(gnutls_pk_params_st * params, + gnutls_datum_t * der) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + const char *oid; + + der->data = NULL; + der->size = 0; + + oid = gnutls_ecc_curve_get_oid(params->curve); + if (oid == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.GOSTParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if ((result = + asn1_write_value(spk, "publicKeyParamSet", oid, + 1)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (params->algo == GNUTLS_PK_GOST_01) + oid = HASH_OID_GOST_R_3411_94_CRYPTOPRO_PARAMS; + else if (params->algo == GNUTLS_PK_GOST_12_256) + oid = HASH_OID_STREEBOG_256; + else if (params->algo == GNUTLS_PK_GOST_12_512) + oid = HASH_OID_STREEBOG_512; + else { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if ((result = + asn1_write_value(spk, "digestParamSet", oid, + 1)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + oid = gnutls_gost_paramset_get_oid(params->gost_params); + if (oid == NULL) { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if (params->algo == GNUTLS_PK_GOST_01) { + if (params->gost_params == GNUTLS_GOST_PARAMSET_CP_A) + oid = NULL; + } else { + if (params->gost_params == GNUTLS_GOST_PARAMSET_TC26_Z) + oid = NULL; + } + + if ((result = + asn1_write_value(spk, "encryptionParamSet", oid, + oid ? 1 : 0)) != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + asn1_delete_structure(&spk); + return result; +} + /* * This function writes the public parameters for DSS keys. * Needs 1 parameter (y). diff --git a/lib/x509/output.c b/lib/x509/output.c index b9e183d37f..4fc37d6253 100644 --- a/lib/x509/output.c +++ b/lib/x509/output.c @@ -1404,6 +1404,60 @@ print_pubkey(gnutls_buffer_st * str, const char *key_name, } break; + case GNUTLS_PK_GOST_01: + case GNUTLS_PK_GOST_12_256: + case GNUTLS_PK_GOST_12_512: + { + gnutls_datum_t x, y; + gnutls_ecc_curve_t curve; + gnutls_digest_algorithm_t digest; + gnutls_gost_paramset_t param; + + err = + gnutls_pubkey_export_gost_raw2(pubkey, &curve, + &digest, + ¶m, + &x, &y, 0); + if (err < 0) + addf(str, "error: get_pk_gost_raw: %s\n", + gnutls_strerror(err)); + else { + addf(str, _("\t\tCurve:\t%s\n"), + gnutls_ecc_curve_get_name(curve)); + addf(str, _("\t\tDigest:\t%s\n"), + gnutls_digest_get_name(digest)); + addf(str, _("\t\tParamSet: %s\n"), + gnutls_gost_paramset_get_name(param)); + if (format == + GNUTLS_CRT_PRINT_FULL_NUMBERS) { + adds(str, _("\t\tX: ")); + _gnutls_buffer_hexprint(str, + x.data, + x.size); + adds(str, "\n"); + adds(str, _("\t\tY: ")); + _gnutls_buffer_hexprint(str, + y.data, + y.size); + adds(str, "\n"); + } else { + adds(str, _("\t\tX:\n")); + _gnutls_buffer_hexdump(str, x.data, + x.size, + "\t\t\t"); + adds(str, _("\t\tY:\n")); + _gnutls_buffer_hexdump(str, y.data, + y.size, + "\t\t\t"); + } + + gnutls_free(x.data); + gnutls_free(y.data); + + } + } + break; + default: break; } @@ -1768,6 +1822,15 @@ static void print_keyid(gnutls_buffer_st * str, gnutls_x509_crt_t cert) if (err < 0) return; + name = gnutls_ecc_curve_get_name(curve); + bits = 0; + } else if (IS_GOSTEC(err)) { + gnutls_ecc_curve_t curve; + + err = gnutls_x509_crt_get_pk_gost_raw(cert, &curve, NULL, NULL, NULL, NULL); + if (err < 0) + return; + name = gnutls_ecc_curve_get_name(curve); bits = 0; } else { diff --git a/lib/x509/x509.c b/lib/x509/x509.c index 74040a4e9d..721a1a0a93 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -3522,6 +3522,59 @@ gnutls_x509_crt_get_pk_ecc_raw(gnutls_x509_crt_t crt, return ret; } +/** + * gnutls_x509_crt_get_pk_gost_raw: + * @crt: Holds the certificate + * @curve: will hold the curve + * @paramset: will hold paramset + * @x: will hold x + * @y: will hold y + * + * This function will export the GOST public key's parameters found in + * the given certificate. The new parameters will be allocated using + * gnutls_malloc() and will be stored in the appropriate datum. + * + * Returns: %GNUTLS_E_SUCCESS on success, otherwise a negative error code. + * + * Since: 3.6.3 + **/ +int +gnutls_x509_crt_get_pk_gost_raw(gnutls_x509_crt_t crt, + gnutls_ecc_curve_t * curve, + gnutls_digest_algorithm_t * digest, + gnutls_gost_paramset_t *paramset, + gnutls_datum_t * x, gnutls_datum_t * y) +{ + int ret; + gnutls_pubkey_t pubkey; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = gnutls_pubkey_init(&pubkey); + if (ret < 0) + return gnutls_assert_val(ret); + + ret = gnutls_pubkey_import_x509(pubkey, crt, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_pubkey_export_gost_raw2(pubkey, curve, digest, + paramset, x, y, 0); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + cleanup: + gnutls_pubkey_deinit(pubkey); + return ret; +} + /** * gnutls_x509_crt_get_pk_dsa_raw: * @crt: Holds the certificate diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 8f8733b358..a38ed8a3fc 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -262,6 +262,10 @@ int _gnutls_x509_read_ecc_params(uint8_t * der, int dersize, unsigned int *curve); +int +_gnutls_x509_read_gost_params(uint8_t * der, int dersize, + gnutls_pk_params_st * params); + int _gnutls_asn1_encode_privkey(ASN1_TYPE * c2, gnutls_pk_params_st * params); diff --git a/symbols.last b/symbols.last index 9995f36d08..ac98a2a5fc 100644 --- a/symbols.last +++ b/symbols.last @@ -623,6 +623,7 @@ gnutls_pubkey_export_dsa_raw@GNUTLS_3_4 gnutls_pubkey_export_ecc_raw2@GNUTLS_3_6_0 gnutls_pubkey_export_ecc_raw@GNUTLS_3_4 gnutls_pubkey_export_ecc_x962@GNUTLS_3_4 +gnutls_pubkey_export_gost_raw2@GNUTLS_3_6_3 gnutls_pubkey_export_rsa_raw2@GNUTLS_3_6_0 gnutls_pubkey_export_rsa_raw@GNUTLS_3_4 gnutls_pubkey_get_key_id@GNUTLS_3_4 @@ -635,6 +636,7 @@ gnutls_pubkey_import@GNUTLS_3_4 gnutls_pubkey_import_dsa_raw@GNUTLS_3_4 gnutls_pubkey_import_ecc_raw@GNUTLS_3_4 gnutls_pubkey_import_ecc_x962@GNUTLS_3_4 +gnutls_pubkey_import_gost_raw@GNUTLS_3_6_3 gnutls_pubkey_import_openpgp@GNUTLS_3_4 gnutls_pubkey_import_openpgp_raw@GNUTLS_3_4 gnutls_pubkey_import_pkcs11@GNUTLS_3_4 @@ -980,6 +982,7 @@ gnutls_x509_crt_get_name_constraints@GNUTLS_3_4 gnutls_x509_crt_get_pk_algorithm@GNUTLS_3_4 gnutls_x509_crt_get_pk_dsa_raw@GNUTLS_3_4 gnutls_x509_crt_get_pk_ecc_raw@GNUTLS_3_4 +gnutls_x509_crt_get_pk_gost_raw@GNUTLS_3_6_3 gnutls_x509_crt_get_pk_oid@GNUTLS_3_4 gnutls_x509_crt_get_pk_rsa_raw@GNUTLS_3_4 gnutls_x509_crt_get_policy@GNUTLS_3_4 diff --git a/tests/cert-tests/Makefile.am b/tests/cert-tests/Makefile.am index 150681e325..96b6d087cd 100644 --- a/tests/cert-tests/Makefile.am +++ b/tests/cert-tests/Makefile.am @@ -43,7 +43,8 @@ EXTRA_DIST = data/ca-no-pathlen.pem data/no-ca-or-pathlen.pem data/aki-cert.pem data/template-krb5name.pem data/template-krb5name-full.pem data/template-test-ecc.key \ data/template-rsa-sha3-256.pem data/template-rsa-sha3-512.pem data/template-rsa-sha3-224.pem \ data/template-rsa-sha3-384.pem data/long-oids.pem \ - data/name-constraints-ip2.pem data/chain-md5.pem data/gost-cert.pem \ + data/name-constraints-ip2.pem data/chain-md5.pem \ + data/gost-cert.pem data/gost-cert-nogost.pem data/gost94-cert.pem \ templates/template-tlsfeature.tmpl data/userid.pem data/cert-with-crl.p12 \ data/template-tlsfeature.pem data/template-tlsfeature.csr \ templates/template-tlsfeature-crq.tmpl templates/arb-extensions.tmpl data/arb-extensions.pem \ @@ -141,5 +142,11 @@ if WINDOWS TESTS_ENVIRONMENT += WINDOWS=1 endif +if ENABLE_GOST +TESTS_ENVIRONMENT += ENABLE_GOST=1 +else +TESTS_ENVIRONMENT += ENABLE_GOST=0 +endif + distclean-local: rm -rf tmp-* *.tmp diff --git a/tests/cert-tests/data/gost-cert-nogost.pem b/tests/cert-tests/data/gost-cert-nogost.pem new file mode 100644 index 0000000000..bf280349fd --- /dev/null +++ b/tests/cert-tests/data/gost-cert-nogost.pem @@ -0,0 +1,45 @@ +X.509 Certificate Information: + Version: 3 + Serial Number (hex): 011f + Issuer: CN=SuperPlat CA 01,OU=SuperPlat CA,O=SuperPlat,L=Moscow,ST=Russia,C=RU + Validity: + Not Before: Fri Aug 17 06:47:36 UTC 2012 + Not After: Sat Aug 17 06:47:36 UTC 2013 + Subject: CN=SuperTerm0000001,OU=SuperPlat Terminals,O=SuperPlat,L=Moscow,ST=Russia,C=RU + Subject Public Key Algorithm: GOST R 34.10-2001 + Extensions: + Basic Constraints (not critical): + Certificate Authority (CA): FALSE + Unknown extension 2.16.840.1.113730.1.13 (not critical): + ASCII: ..OpenSSL Generated Certificate + Hexdump: 161d4f70656e53534c2047656e657261746564204365727469666963617465 + Subject Key Identifier (not critical): + 43fe227895724f4e3a74f264e4fd0b800c082e03 + Authority Key Identifier (not critical): + 9875a3b785c1641b23344d9bfbae0c2a256b44eb + Signature Algorithm: GOSTR341001 + Signature: + 8f:37:24:fd:be:f0:37:d9:f3:1a:5c:31:5e:33:ef:35 + 61:93:07:03:3d:4d:e8:2c:1b:39:a2:6c:d4:2f:85:35 + b2:43:1d:ed:b5:15:45:c7:10:38:41:28:68:29:62:20 + e6:92:8a:64:34:87:b8:b9:9f:ab:c8:04:6d:26:55:99 +Other Information: + Fingerprint: + sha1:621f34c4fdd7e93f9b8f18224ba0bcd1c63a4771 + sha256:ac6ecf4e7a876edf3e61f538d6061353c2015bfbdf60370492f7404d7f09e13a + +-----BEGIN CERTIFICATE----- +MIICXjCCAgugAwIBAgICAR8wCgYGKoUDAgIDBQAwdDELMAkGA1UEBhMCUlUxDzAN +BgNVBAgMBlJ1c3NpYTEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlTdXBlclBs +YXQxFTATBgNVBAsMDFN1cGVyUGxhdCBDQTEYMBYGA1UEAwwPU3VwZXJQbGF0IENB +IDAxMB4XDTEyMDgxNzA2NDczNloXDTEzMDgxNzA2NDczNlowfDELMAkGA1UEBhMC +UlUxDzANBgNVBAgMBlJ1c3NpYTEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlT +dXBlclBsYXQxHDAaBgNVBAsME1N1cGVyUGxhdCBUZXJtaW5hbHMxGTAXBgNVBAMM +EFN1cGVyVGVybTAwMDAwMDEwYzAcBgYqhQMCAhMwEgYHKoUDAgIjAQYHKoUDAgIe +AQNDAARA69rbaWL2GSV1NVaWMSrWRX8d/frrwbVjJerPQKjyNeDYZxgSjTTp3dck +6fQLx2OjQsu6n+vdyBPQex/iwbJBV6N7MHkwCQYDVR0TBAIwADAsBglghkgBhvhC +AQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFEP+ +IniVck9OOnTyZOT9C4AMCC4DMB8GA1UdIwQYMBaAFJh1o7eFwWQbIzRNm/uuDCol +a0TrMAoGBiqFAwICAwUAA0EAjzck/b7wN9nzGlwxXjPvNWGTBwM9TegsGzmibNQv +hTWyQx3ttRVFxxA4QShoKWIg5pKKZDSHuLmfq8gEbSZVmQ== +-----END CERTIFICATE----- diff --git a/tests/cert-tests/data/gost-cert.pem b/tests/cert-tests/data/gost-cert.pem index bf280349fd..1501f83c4c 100644 --- a/tests/cert-tests/data/gost-cert.pem +++ b/tests/cert-tests/data/gost-cert.pem @@ -7,6 +7,17 @@ X.509 Certificate Information: Not After: Sat Aug 17 06:47:36 UTC 2013 Subject: CN=SuperTerm0000001,OU=SuperPlat Terminals,O=SuperPlat,L=Moscow,ST=Russia,C=RU Subject Public Key Algorithm: GOST R 34.10-2001 + Algorithm Security Level: High (256 bits) + Curve: CryptoPro-A + Digest: GOSTR341194 + ParamSet: CryptoPro-A + X: + 00:e0:35:f2:a8:40:cf:ea:25:63:b5:c1:eb:fa:fd:1d + 7f:45:d6:2a:31:96:56:35:75:25:19:f6:62:69:db:da + eb + Y: + 57:41:b2:c1:e2:1f:7b:d0:13:c8:dd:eb:9f:ba:cb:42 + a3:63:c7:0b:f4:e9:24:d7:dd:e9:34:8d:12:18:67:d8 Extensions: Basic Constraints (not critical): Certificate Authority (CA): FALSE @@ -27,6 +38,11 @@ Other Information: Fingerprint: sha1:621f34c4fdd7e93f9b8f18224ba0bcd1c63a4771 sha256:ac6ecf4e7a876edf3e61f538d6061353c2015bfbdf60370492f7404d7f09e13a + Public Key ID: + sha1:43757042dae9e9f5fa92cc2d2cbf4950f28a7bd0 + sha256:cee4a59e7803bafb101af8e39e5355d7895e3b85e7616fe624d48f2c51e8bdbf + Public Key PIN: + pin-sha256:zuSlnngDuvsQGvjjnlNV14leO4XnYW/mJNSPLFHovb8= -----BEGIN CERTIFICATE----- MIICXjCCAgugAwIBAgICAR8wCgYGKoUDAgIDBQAwdDELMAkGA1UEBhMCUlUxDzAN diff --git a/tests/cert-tests/data/gost94-cert.pem b/tests/cert-tests/data/gost94-cert.pem new file mode 100644 index 0000000000..f4d63fb9d1 --- /dev/null +++ b/tests/cert-tests/data/gost94-cert.pem @@ -0,0 +1,33 @@ +X.509 Certificate Information: + Version: 1 + Serial Number (hex): 230ee360469524cec70be494182e7eeb + Issuer: EMAIL=GostR3410-94@example.com,C=RU,O=CryptoPro,CN=GostR3410-94 example + Validity: + Not Before: Tue Aug 16 12:32:50 UTC 2005 + Not After: Sun Aug 16 12:32:50 UTC 2015 + Subject: EMAIL=GostR3410-94@example.com,C=RU,O=CryptoPro,CN=GostR3410-94 example + Subject Public Key Algorithm: 1.2.643.2.2.20 + Signature Algorithm: 1.2.643.2.2.4 + Signature: + 11:c7:08:7e:12:dc:02:f1:02:23:29:47:76:8f:47:2a + 81:83:50:e3:07:cc:f2:e4:31:23:89:42:c8:73:e1:de + 22:f7:85:f3:55:bd:94:ec:46:91:9c:67:ac:58:d7:05 + 2a:a7:8c:b7:85:2a:01:75:85:f7:d7:38:03:fb:cd:43 +Other Information: + Fingerprint: + sha1:d43782a1f943a966f4ea1ac96bd048fe68d4d951 + sha256:19260c765a2c820be3612dc0431c045d37570f8e4de58ba218f10a8eeb0d42d7 + +-----BEGIN CERTIFICATE----- +MIICCzCCAboCECMO42BGlSTOxwvklBgufuswCAYGKoUDAgIEMGkxHTAbBgNVBAMM +FEdvc3RSMzQxMC05NCBleGFtcGxlMRIwEAYDVQQKDAlDcnlwdG9Qcm8xCzAJBgNV +BAYTAlJVMScwJQYJKoZIhvcNAQkBFhhHb3N0UjM0MTAtOTRAZXhhbXBsZS5jb20w +HhcNMDUwODE2MTIzMjUwWhcNMTUwODE2MTIzMjUwWjBpMR0wGwYDVQQDDBRHb3N0 +UjM0MTAtOTQgZXhhbXBsZTESMBAGA1UECgwJQ3J5cHRvUHJvMQswCQYDVQQGEwJS +VTEnMCUGCSqGSIb3DQEJARYYR29zdFIzNDEwLTk0QGV4YW1wbGUuY29tMIGlMBwG +BiqFAwICFDASBgcqhQMCAiACBgcqhQMCAh4BA4GEAASBgLuEZuF5nls02CyAfxOo +GWZxV/6MVCUhR28wCyd3RpjG+0dVvrey85NsObVCNyaE4g0QiiQOHwxCTSs7ESuo +v2Y5MlyUi8Go/htjEvYJJYfMdRv05YmKCYJo01x3pg+2kBATjeM+fJyR1qwNCCw+ +eMG1wra3Gqgqi0WBkzIydvp7MAgGBiqFAwICBANBABHHCH4S3ALxAiMpR3aPRyqB +g1DjB8zy5DEjiULIc+HeIveF81W9lOxGkZxnrFjXBSqnjLeFKgF1hffXOAP7zUM= +-----END CERTIFICATE----- diff --git a/tests/cert-tests/pem-decoding b/tests/cert-tests/pem-decoding index a31f412b48..0222ae72af 100755 --- a/tests/cert-tests/pem-decoding +++ b/tests/cert-tests/pem-decoding @@ -124,7 +124,13 @@ fi #check whether the cert with GOST parameters is decoded as expected -${VALGRIND} "${CERTTOOL}" --certificate-info --infile "${srcdir}/data/gost-cert.pem" >${TMPFILE} +if test "${ENABLE_GOST}" = "1"; then + GOSTCERT="${srcdir}/data/gost-cert.pem" +else + GOSTCERT="${srcdir}/data/gost-cert-nogost.pem" +fi + +${VALGRIND} "${CERTTOOL}" --certificate-info --infile "${GOSTCERT}" >${TMPFILE} rc=$? if test "${rc}" != "0"; then @@ -132,7 +138,7 @@ if test "${rc}" != "0"; then exit ${rc} fi -${DIFF} -I 'Algorithm Security Level' ${TMPFILE} "${srcdir}/data/gost-cert.pem" || ${DIFF} -I 'Algorithm Security Level' --strip-trailing-cr "${TMPFILE}" "${srcdir}/data/gost-cert.pem" +${DIFF} -u ${TMPFILE} "${GOSTCERT}" || ${DIFF} -u --strip-trailing-cr "${TMPFILE}" "${GOSTCERT}" rc=$? if test "${rc}" != "0"; then @@ -140,6 +146,23 @@ if test "${rc}" != "0"; then exit ${rc} fi +#check whether the cert with GOST 31.10/11-94 parameters is decoded as expected +${VALGRIND} "${CERTTOOL}" --certificate-info --infile "${srcdir}/data/gost94-cert.pem" >${TMPFILE} +rc=$? + +if test "${rc}" != "0"; then + echo "GOST94 cert decoding failed 1" + exit ${rc} +fi + +${DIFF} -I 'Algorithm Security Level' ${TMPFILE} "${srcdir}/data/gost94-cert.pem" || ${DIFF} -I 'Algorithm Security Level' --strip-trailing-cr "${TMPFILE}" "${srcdir}/data/gost94-cert.pem" +rc=$? + +if test "${rc}" != "0"; then + echo "GOST94 cert decoding failed 2" + exit ${rc} +fi + ${VALGRIND} "${CERTTOOL}" --certificate-info --infile "${srcdir}/data/multi-value-dn.pem" >${TMPFILE} rc=$? -- cgit v1.2.1