From 19945cb637c9def9f79fa2edcab4bc63a5084791 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Thu, 6 May 2021 14:10:46 -0400 Subject: algorithms: Explicitly name ECDH_X448_OID and ECDH_X25519_OID Signed-off-by: Daniel Kahn Gillmor --- lib/algorithms/ecc.c | 2 ++ lib/algorithms/publickey.c | 4 ++-- lib/x509/common.h | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/algorithms/ecc.c b/lib/algorithms/ecc.c index 917f83a624..adab4e1c18 100644 --- a/lib/algorithms/ecc.c +++ b/lib/algorithms/ecc.c @@ -81,6 +81,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = { }, { .name = "X25519", + .oid = ECDH_X25519_OID, .id = GNUTLS_ECC_CURVE_X25519, .group = GNUTLS_GROUP_X25519, .pk = GNUTLS_PK_ECDH_X25519, @@ -98,6 +99,7 @@ gnutls_ecc_curve_entry_st ecc_curves[] = { }, { .name = "X448", + .oid = ECDH_X448_OID, .id = GNUTLS_ECC_CURVE_X448, .pk = GNUTLS_PK_ECDH_X448, .size = 56, diff --git a/lib/algorithms/publickey.c b/lib/algorithms/publickey.c index c298a38936..b4cd6b1df0 100644 --- a/lib/algorithms/publickey.c +++ b/lib/algorithms/publickey.c @@ -146,9 +146,9 @@ static const gnutls_pk_entry pk_algorithms[] = { .curve = GNUTLS_ECC_CURVE_ED448, .no_prehashed = 1 }, { .name = "DH", .oid = NULL, .id = GNUTLS_PK_DH, .curve = GNUTLS_ECC_CURVE_INVALID }, - { .name = "ECDH (X25519)", .oid = "1.3.101.110", .id = GNUTLS_PK_ECDH_X25519, + { .name = "ECDH (X25519)", .oid = ECDH_X25519_OID, .id = GNUTLS_PK_ECDH_X25519, .curve = GNUTLS_ECC_CURVE_X25519 }, - { .name = "ECDH (X448)", .oid = "1.3.101.111", .id = GNUTLS_PK_ECDH_X448, + { .name = "ECDH (X448)", .oid = ECDH_X448_OID, .id = GNUTLS_PK_ECDH_X448, .curve = GNUTLS_ECC_CURVE_X448 }, { .name = "UNKNOWN", .oid = NULL, .id = GNUTLS_PK_UNKNOWN, .curve = GNUTLS_ECC_CURVE_INVALID }, diff --git a/lib/x509/common.h b/lib/x509/common.h index 4690d68825..c1df15980f 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -97,6 +97,9 @@ #define SIG_RSA_SHA3_384_OID "2.16.840.1.101.3.4.3.15" #define SIG_RSA_SHA3_512_OID "2.16.840.1.101.3.4.3.16" +#define ECDH_X25519_OID "1.3.101.110" +#define ECDH_X448_OID "1.3.101.111" + #define SIG_EDDSA_SHA512_OID "1.3.101.112" #define SIG_ED448_OID "1.3.101.113" -- cgit v1.2.1 From 41754922aa6d4a0a5cbf1afd03950fe9a359c371 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 19:34:59 -0400 Subject: lib/algorithms: add modern ecdh functions comparable to curve_is_eddsa This is useful for the so-called CFRG curves used in ECDH, x25519 and x448. Signed-off-by: Daniel Kahn Gillmor --- lib/algorithms.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/lib/algorithms.h b/lib/algorithms.h index 7a051b6365..5172bd2784 100644 --- a/lib/algorithms.h +++ b/lib/algorithms.h @@ -475,12 +475,28 @@ inline static int _curve_is_eddsa(const gnutls_ecc_curve_entry_st * e) return 0; } +inline static int _curve_is_modern_ecdh(const gnutls_ecc_curve_entry_st * e) +{ + if (unlikely(e == NULL)) + return 0; + if (e->pk == GNUTLS_PK_ECDH_X25519 || + e->pk == GNUTLS_PK_ECDH_X448) + return 1; + return 0; +} + inline static int curve_is_eddsa(gnutls_ecc_curve_t id) { const gnutls_ecc_curve_entry_st *e = _gnutls_ecc_curve_get_params(id); return _curve_is_eddsa(e); } +inline static int curve_is_modern_ecdh(gnutls_ecc_curve_t id) +{ + const gnutls_ecc_curve_entry_st *e = _gnutls_ecc_curve_get_params(id); + return _curve_is_modern_ecdh(e); +} + static inline int _gnutls_kx_is_ecc(gnutls_kx_algorithm_t kx) { if (kx == GNUTLS_KX_ECDHE_RSA || kx == GNUTLS_KX_ECDHE_ECDSA || -- cgit v1.2.1 From 51ae14728aff9e0bd9d875d77327c6a2dfc575be Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 19:36:14 -0400 Subject: lib/pk: treat modern ECDH octet streams the same way as eddsa streams. Signed-off-by: Daniel Kahn Gillmor --- lib/pk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pk.c b/lib/pk.c index f932589ede..fcf3af1989 100644 --- a/lib/pk.c +++ b/lib/pk.c @@ -1031,7 +1031,7 @@ int _gnutls_params_get_ecc_raw(const gnutls_pk_params_st* params, e = _gnutls_ecc_curve_get_params(params->curve); - if (_curve_is_eddsa(e)) { + if (_curve_is_eddsa(e) || _curve_is_modern_ecdh(e)) { if (x) { ret = _gnutls_set_datum(x, params->raw_pub.data, params->raw_pub.size); if (ret < 0) { -- cgit v1.2.1 From ddb527ea6ac199b7735ab8943a06562b9d9d3135 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 19:48:10 -0400 Subject: nettle: extend pk_verify_priv_params to handle X25519 and X448 This is basically a copy of the EdDSA case in the switch statement. Another way to implement it would be to augment the EdDSA case (and the functions it uses) to have that case also handle ECDH use of the CFRG curves. Signed-off-by: Daniel Kahn Gillmor --- lib/nettle/pk.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index 16d9b4a04c..a1642d0f35 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -831,6 +831,19 @@ get_eddsa_curve(gnutls_pk_algorithm_t algo) } } +static inline gnutls_ecc_curve_t +get_ecdh_curve(gnutls_pk_algorithm_t algo) +{ + switch (algo) { + case GNUTLS_PK_ECDH_X25519: + return GNUTLS_ECC_CURVE_X25519; + case GNUTLS_PK_ECDH_X448: + return GNUTLS_ECC_CURVE_X448; + default: + return gnutls_assert_val(GNUTLS_ECC_CURVE_INVALID); + } +} + static inline int eddsa_sign(gnutls_pk_algorithm_t algo, const uint8_t *pub, @@ -3098,6 +3111,34 @@ wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo, ret = 0; break; } + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: { + gnutls_ecc_curve_t curve; + const gnutls_ecc_curve_entry_st *e; + uint8_t pub[57]; /* can accommodate both curves */ + + curve = get_ecdh_curve(algo); + e = _gnutls_ecc_curve_get_params(curve); + if (e == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (params->raw_pub.data == NULL) { + return 0; /* nothing to verify */ + } + + if (params->raw_pub.size != e->size) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + ret = edwards_curve_mul_g(algo, pub, params->raw_priv.data); + if (ret < 0) + return ret; + + if (memcmp(params->raw_pub.data, pub, e->size) != 0) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + ret = 0; + break; + } #if ENABLE_GOST case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: -- cgit v1.2.1 From 7d895c807989f640e5a5c3c58ab4f3c0cb0215f4 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 20:12:15 -0400 Subject: pubkey: handle X25519 and X448 in gnutls_pubkey_import_pkcs11 I am not confident in the strings I chose to match on in ASN1_ETYPE_PRINTABLE_STRING, in that I do not know what registry I should look this up in. The *parse_ecc_ecdh_params and *import_ecc_ecdh functions are tweaked analogs to the eddsa versions of those functions. Signed-off-by: Daniel Kahn Gillmor --- lib/pubkey.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/lib/pubkey.c b/lib/pubkey.c index a1735cf766..6d00e87876 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -497,6 +497,139 @@ gnutls_pubkey_import_ecc_eddsa(gnutls_pubkey_t key, return ret; } +/* Same as above, but for Edwards key agreement */ +static int +gnutls_pubkey_parse_ecc_ecdh_params(const gnutls_datum_t *parameters, + gnutls_ecc_curve_t *outcurve) +{ + gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID; + ASN1_TYPE asn1 = ASN1_TYPE_EMPTY; + unsigned int etype = ASN1_ETYPE_INVALID; + char str[MAX_OID_SIZE]; + int str_size; + int ret; + + ret = asn1_create_element(_gnutls_get_gnutls_asn(), + "GNUTLS.pkcs-11-ec-Parameters", &asn1); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(ret); + } + + ret = asn1_der_decoding(&asn1, parameters->data, parameters->size, + NULL); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + + /* Read the type of choice. + */ + str_size = sizeof(str) - 1; + ret = asn1_read_value(asn1, "", str, &str_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + goto cleanup; + } + str[str_size] = 0; + + /* Convert the choice to enum type */ + if (strcmp(str, "oId") == 0) { + etype = ASN1_ETYPE_OBJECT_ID; + } else if (strcmp(str, "curveName") == 0) { + etype = ASN1_ETYPE_PRINTABLE_STRING; + } + + str_size = sizeof(str) - 1; + switch (etype) { + case ASN1_ETYPE_OBJECT_ID: + ret = asn1_read_value(asn1, "oId", str, &str_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + break; + } + + curve = gnutls_oid_to_ecc_curve(str); + if (curve != GNUTLS_ECC_CURVE_X25519 && + curve != GNUTLS_ECC_CURVE_X448) { + _gnutls_debug_log("Curve %s is not supported for Edwards-based key agreement\n", str); + gnutls_assert(); + curve = GNUTLS_ECC_CURVE_INVALID; + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + break; + } + + ret = GNUTLS_E_SUCCESS; + break; + + case ASN1_ETYPE_PRINTABLE_STRING: + ret = asn1_read_value(asn1, "curveName", str, &str_size); + if (ret != ASN1_SUCCESS) { + gnutls_assert(); + ret = _gnutls_asn2err(ret); + break; + } + + if (str_size == strlen("x25519") && + strncmp(str, "x25519", str_size) == 0) { + curve = GNUTLS_ECC_CURVE_X25519; + ret = GNUTLS_E_SUCCESS; + break; + } else if (str_size == strlen("x448") && + strncmp(str, "x448", str_size) == 0) { + curve = GNUTLS_ECC_CURVE_X448; + ret = GNUTLS_E_SUCCESS; + break; + } + /* FALLTHROUGH */ + + default: + /* Neither of CHOICEs found. Fail */ + gnutls_assert(); + ret = GNUTLS_E_ECC_UNSUPPORTED_CURVE; + curve = GNUTLS_ECC_CURVE_INVALID; + break; + } + + + cleanup: + asn1_delete_structure(&asn1); + *outcurve = curve; + return ret; +} + +static int +gnutls_pubkey_import_ecc_ecdh(gnutls_pubkey_t key, + const gnutls_datum_t * parameters, + const gnutls_datum_t * ecpoint) +{ + int ret; + + gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID; + gnutls_datum_t raw_point = {NULL, 0}; + + ret = gnutls_pubkey_parse_ecc_ecdh_params(parameters, &curve); + if (ret < 0) { + return gnutls_assert_val(ret); + } + + ret = _gnutls_x509_decode_string(ASN1_ETYPE_OCTET_STRING, + ecpoint->data, ecpoint->size, + &raw_point, 0); + if (ret < 0) { + gnutls_assert(); + gnutls_free(raw_point.data); + return ret; + } + ret = gnutls_pubkey_import_ecc_raw(key, curve, &raw_point, NULL); + + gnutls_free(raw_point.data); + return ret; +} + /** * gnutls_pubkey_import_pkcs11: * @key: The public key @@ -577,6 +710,10 @@ gnutls_pubkey_import_pkcs11(gnutls_pubkey_t key, ret = gnutls_pubkey_import_ecc_eddsa(key, &obj->pubkey[0], &obj->pubkey[1]); break; + case GNUTLS_PK_ECDH_X25519: + ret = gnutls_pubkey_import_ecc_ecdh(key, &obj->pubkey[0], + &obj->pubkey[1]); + break; default: gnutls_assert(); return GNUTLS_E_UNIMPLEMENTED_FEATURE; -- cgit v1.2.1 From 81db94c848ea1cf2f3741a43b334979009da797c Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 20:14:07 -0400 Subject: x509: handle X448 and X25519 in write_pubkey This uses the same structure as _gnutls_x509_write_eddsa_pubkey. Another way to write this would be to combine those two functions, despite X448 and X25519 not being EdDSA at all. Signed-off-by: Daniel Kahn Gillmor --- lib/x509/key_encode.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/x509/key_encode.c b/lib/x509/key_encode.c index c3ff2a9b05..a1abbe621c 100644 --- a/lib/x509/key_encode.c +++ b/lib/x509/key_encode.c @@ -161,6 +161,35 @@ _gnutls_x509_write_eddsa_pubkey(const gnutls_pk_params_st * params, return 0; } +/* + * some x509 certificate functions that relate to MPI parameter + * setting. This writes a raw public key. + * + * Allocates the space used to store the data. + */ +static int +_gnutls_x509_write_modern_ecdh_pubkey(const gnutls_pk_params_st * params, + gnutls_datum_t * raw) +{ + int ret; + + raw->data = NULL; + raw->size = 0; + + if (params->raw_pub.size == 0) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (params->curve != GNUTLS_ECC_CURVE_X25519 && + params->curve != GNUTLS_ECC_CURVE_X448) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + ret = _gnutls_set_datum(raw, params->raw_pub.data, params->raw_pub.size); + if (ret < 0) + return gnutls_assert_val(ret); + + return 0; +} + int _gnutls_x509_write_gost_pubkey(const gnutls_pk_params_st * params, gnutls_datum_t * der) @@ -282,6 +311,9 @@ _gnutls_x509_write_pubkey(const gnutls_pk_params_st * params, case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: return _gnutls_x509_write_eddsa_pubkey(params, der); + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + return _gnutls_x509_write_modern_ecdh_pubkey(params, der); case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: case GNUTLS_PK_GOST_12_512: -- cgit v1.2.1 From f0a9af880a618772fd81584355fa0657639fcbf8 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 19:53:28 -0400 Subject: Enable X25519 and X448 everywhere that EdDSA is supported. These are just trivial extension points where the codepath is the same for the ECDH scheme as it is for the EdDSA scheme. Signed-off-by: Daniel Kahn Gillmor --- lib/nettle/pk.c | 2 ++ lib/pk.c | 2 ++ lib/privkey.c | 2 ++ lib/pubkey.c | 6 +++++- lib/x509/key_decode.c | 2 ++ lib/x509/key_encode.c | 7 ++++++- lib/x509/output.c | 2 ++ lib/x509/privkey_pkcs8.c | 2 ++ src/certtool-common.c | 4 +++- src/certtool-common.h | 1 + 10 files changed, 27 insertions(+), 3 deletions(-) diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index a1642d0f35..ec2f1e5df6 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -1802,6 +1802,8 @@ wrap_nettle_pk_generate_params(gnutls_pk_algorithm_t algo, case GNUTLS_PK_ECDSA: case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: #if ENABLE_GOST case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: diff --git a/lib/pk.c b/lib/pk.c index fcf3af1989..1904f59453 100644 --- a/lib/pk.c +++ b/lib/pk.c @@ -1216,6 +1216,8 @@ pk_prepare_hash(gnutls_pk_algorithm_t pk, case GNUTLS_PK_ECDSA: case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: case GNUTLS_PK_GOST_12_512: diff --git a/lib/privkey.c b/lib/privkey.c index 0e8d29561f..7b983b145d 100644 --- a/lib/privkey.c +++ b/lib/privkey.c @@ -206,6 +206,8 @@ privkey_to_pubkey(gnutls_pk_algorithm_t pk, break; case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: ret = _gnutls_set_datum(&pub->raw_pub, priv->raw_pub.data, priv->raw_pub.size); if (ret < 0) return gnutls_assert_val(ret); diff --git a/lib/pubkey.c b/lib/pubkey.c index 6d00e87876..14171f68d7 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -62,6 +62,8 @@ unsigned pubkey_to_bits(const gnutls_pk_params_st * params) case GNUTLS_PK_ECDSA: case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: case GNUTLS_PK_GOST_12_512: @@ -1175,7 +1177,9 @@ gnutls_pubkey_export_ecc_raw2(gnutls_pubkey_t key, *curve = key->params.curve; if (key->params.algo == GNUTLS_PK_EDDSA_ED25519 || - key->params.algo == GNUTLS_PK_EDDSA_ED448) { + key->params.algo == GNUTLS_PK_EDDSA_ED448 || + key->params.algo == GNUTLS_PK_ECDH_X25519 || + key->params.algo == GNUTLS_PK_ECDH_X448) { if (x) { ret = _gnutls_set_datum(x, key->params.raw_pub.data, key->params.raw_pub.size); if (ret < 0) diff --git a/lib/x509/key_decode.c b/lib/x509/key_decode.c index c7e69d8e1f..ea241163b9 100644 --- a/lib/x509/key_decode.c +++ b/lib/x509/key_decode.c @@ -635,6 +635,8 @@ int _gnutls_x509_check_pubkey_params(gnutls_pk_params_st * params) case GNUTLS_PK_ECDSA: case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: case GNUTLS_PK_GOST_12_512: diff --git a/lib/x509/key_encode.c b/lib/x509/key_encode.c index a1abbe621c..8428cd1733 100644 --- a/lib/x509/key_encode.c +++ b/lib/x509/key_encode.c @@ -283,6 +283,8 @@ _gnutls_x509_write_pubkey_params(const gnutls_pk_params_st * params, return _gnutls_x509_write_ecc_params(params->curve, der); case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: der->data = NULL; der->size = 0; @@ -867,7 +869,8 @@ _gnutls_asn1_encode_ecc(asn1_node * c2, gnutls_pk_params_st * params) goto cleanup; } - if (curve_is_eddsa(params->curve)) { + if (curve_is_eddsa(params->curve) || + curve_is_modern_ecdh(params->curve)) { if (params->raw_pub.size == 0 || params->raw_priv.size == 0) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); ret = @@ -1071,6 +1074,8 @@ int _gnutls_asn1_encode_privkey(asn1_node * c2, case GNUTLS_PK_ECDSA: case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: return _gnutls_asn1_encode_ecc(c2, params); case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: diff --git a/lib/x509/output.c b/lib/x509/output.c index 70210847b8..1e58c3ca92 100644 --- a/lib/x509/output.c +++ b/lib/x509/output.c @@ -1453,6 +1453,8 @@ print_pubkey(gnutls_buffer_st * str, const char *key_name, case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: case GNUTLS_PK_ECDSA: { gnutls_datum_t x, y; diff --git a/lib/x509/privkey_pkcs8.c b/lib/x509/privkey_pkcs8.c index c54ad4a8b2..3c7c9f8eee 100644 --- a/lib/x509/privkey_pkcs8.c +++ b/lib/x509/privkey_pkcs8.c @@ -69,6 +69,8 @@ _encode_privkey(gnutls_x509_privkey_t pkey, gnutls_datum_t * raw) switch (pkey->params.algo) { case GNUTLS_PK_EDDSA_ED25519: case GNUTLS_PK_EDDSA_ED448: + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: /* we encode as octet string (which is going to be stored inside * another octet string). No comments. */ ret = _gnutls_x509_encode_string(ASN1_ETYPE_OCTET_STRING, diff --git a/src/certtool-common.c b/src/certtool-common.c index 31e1c2619f..dba89df9a0 100644 --- a/src/certtool-common.c +++ b/src/certtool-common.c @@ -1289,7 +1289,9 @@ static void privkey_info_int(FILE *outfile, common_info_st * cinfo, } } else if (key_type == GNUTLS_PK_ECDSA || key_type == GNUTLS_PK_EDDSA_ED25519 || - key_type == GNUTLS_PK_EDDSA_ED448) { + key_type == GNUTLS_PK_EDDSA_ED448 || + key_type == GNUTLS_PK_ECDH_X25519 || + key_type == GNUTLS_PK_ECDH_X448) { gnutls_datum_t y, x, k; gnutls_ecc_curve_t curve; diff --git a/src/certtool-common.h b/src/certtool-common.h index 04c7a3e91a..db7b1bde3a 100644 --- a/src/certtool-common.h +++ b/src/certtool-common.h @@ -91,6 +91,7 @@ void switch_to_pkcs8_when_needed(common_info_st *cinfo, gnutls_x509_privkey_t ke return; if (key_type == GNUTLS_PK_RSA_PSS || key_type == GNUTLS_PK_EDDSA_ED25519 || key_type == GNUTLS_PK_EDDSA_ED448 || + key_type == GNUTLS_PK_ECDH_X25519 || key_type == GNUTLS_PK_ECDH_X448 || key_type == GNUTLS_PK_GOST_01 || key_type == GNUTLS_PK_GOST_12_256 || key_type == GNUTLS_PK_GOST_12_512) { if (cinfo->verbose) -- cgit v1.2.1 From 2edf7da98a667491962243e2378c33137cbdfea7 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 20:23:41 -0400 Subject: x509: enable importing secret keys for X448 and X25519. _decode_pkcs8_modern_ecdh_key is virtually the same as _decode_pkcs8_eddsa_key. Another implementation would be to collapse these two functions into one, since their structure is identical. Signed-off-by: Daniel Kahn Gillmor --- lib/x509/privkey_pkcs8.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/lib/x509/privkey_pkcs8.c b/lib/x509/privkey_pkcs8.c index 3c7c9f8eee..4aa8993307 100644 --- a/lib/x509/privkey_pkcs8.c +++ b/lib/x509/privkey_pkcs8.c @@ -1138,6 +1138,56 @@ _decode_pkcs8_eddsa_key(asn1_node pkcs8_asn, gnutls_x509_privkey_t pkey, const c } } +static int +_decode_pkcs8_modern_ecdh_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey, const char *oid) +{ + int ret; + gnutls_datum_t tmp; + gnutls_ecc_curve_t curve = GNUTLS_ECC_CURVE_INVALID; + const gnutls_ecc_curve_entry_st *ce; + + gnutls_pk_params_init(&pkey->params); + + curve = gnutls_oid_to_ecc_curve(oid); + if (curve == GNUTLS_ECC_CURVE_INVALID) { + _gnutls_debug_log("PKCS#8: unknown curve OID %s\n", oid); + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } + + ce = _gnutls_ecc_curve_get_params(curve); + if (_curve_is_modern_ecdh(ce)) { + ret = _gnutls_x509_read_string(pkcs8_asn, "privateKey", &tmp, ASN1_ETYPE_OCTET_STRING, 1); + if (ret < 0) { + gnutls_assert(); + return gnutls_assert_val(ret); + } + + if (tmp.size != ce->size) { + gnutls_free(tmp.data); + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + } + gnutls_free(pkey->params.raw_priv.data); + switch (curve) { + case GNUTLS_ECC_CURVE_X25519: + pkey->params.algo = GNUTLS_PK_ECDH_X25519; + break; + case GNUTLS_ECC_CURVE_X448: + pkey->params.algo = GNUTLS_PK_ECDH_X448; + break; + default: + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + } + pkey->params.raw_priv.data = tmp.data; + pkey->params.raw_priv.size = tmp.size; + pkey->params.curve = curve; + + tmp.data = NULL; + return 0; + } else { + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + } +} + /* Converts a GOST key to * an internal structure (gnutls_private_key) */ @@ -1463,6 +1513,10 @@ decode_private_key_info(const gnutls_datum_t * der, case GNUTLS_PK_EDDSA_ED448: result = _decode_pkcs8_eddsa_key(pkcs8_asn, pkey, oid); break; + case GNUTLS_PK_ECDH_X25519: + case GNUTLS_PK_ECDH_X448: + result = _decode_pkcs8_modern_ecdh_key(pkcs8_asn, pkey, oid); + break; case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: case GNUTLS_PK_GOST_12_512: -- cgit v1.2.1 From c605e559e890ce77ec033a6c1cad819f266401a8 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 21:30:53 -0400 Subject: nettle: handle X25519 and X448 in pk_fixup Signed-off-by: Daniel Kahn Gillmor --- lib/nettle/pk.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index ec2f1e5df6..6af19c459f 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -3506,6 +3506,30 @@ wrap_nettle_pk_fixup(gnutls_pk_algorithm_t algo, return ret; } + params->raw_pub.size = params->raw_priv.size; + } else if (algo == GNUTLS_PK_ECDH_X25519 || + algo == GNUTLS_PK_ECDH_X448) { + if (unlikely(get_ecdh_curve(algo) != params->curve)) + return gnutls_assert_val(GNUTLS_E_ECC_UNSUPPORTED_CURVE); + + if (params->raw_priv.data == NULL) + return gnutls_assert_val(GNUTLS_E_PK_INVALID_PRIVKEY); + + if (params->raw_pub.data == NULL) { + params->raw_pub.data = gnutls_malloc(params->raw_priv.size); + } + + if (params->raw_pub.data == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + ret = edwards_curve_mul_g(algo, + params->raw_pub.data, + params->raw_priv.data); + if (ret < 0) { + gnutls_free(params->raw_pub.data); + return ret; + } + params->raw_pub.size = params->raw_priv.size; } else if (algo == GNUTLS_PK_RSA_PSS) { if (params->params_nr < RSA_PRIVATE_PARAMS - 3) -- cgit v1.2.1 From d6bb88cd7a84874da10fa493841244aad6eae863 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 21:53:47 -0400 Subject: x509: handle X25519 and X448 in read_pubkey _gnutls_x509_read_ecdh_pubkey is basically a clone of _gnutls_x509_read_eddsa_pubkey. Another form of implementation would be to collapse these two static functions into a common function for all "CFRG" curves. Signed-off-by: Daniel Kahn Gillmor --- lib/x509/key_decode.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/lib/x509/key_decode.c b/lib/x509/key_decode.c index ea241163b9..44e4297db0 100644 --- a/lib/x509/key_decode.c +++ b/lib/x509/key_decode.c @@ -41,6 +41,9 @@ static int _gnutls_x509_read_ecc_pubkey(uint8_t * der, int dersize, static int _gnutls_x509_read_eddsa_pubkey(gnutls_ecc_curve_t curve, uint8_t * der, int dersize, gnutls_pk_params_st * params); +static int _gnutls_x509_read_ecdh_pubkey(gnutls_ecc_curve_t curve, + 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); @@ -125,6 +128,17 @@ int _gnutls_x509_read_eddsa_pubkey(gnutls_ecc_curve_t curve, return _gnutls_set_datum(¶ms->raw_pub, der, dersize); } +int _gnutls_x509_read_ecdh_pubkey(gnutls_ecc_curve_t curve, + uint8_t * der, int dersize, + gnutls_pk_params_st * params) +{ + int size = gnutls_ecc_curve_get_size(curve); + if (dersize != size) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + 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 @@ -564,6 +578,12 @@ int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der, case GNUTLS_PK_EDDSA_ED448: ret = _gnutls_x509_read_eddsa_pubkey(GNUTLS_ECC_CURVE_ED448, der, dersize, params); break; + case GNUTLS_PK_ECDH_X25519: + ret = _gnutls_x509_read_ecdh_pubkey(GNUTLS_ECC_CURVE_X25519, der, dersize, params); + break; + case GNUTLS_PK_ECDH_X448: + ret = _gnutls_x509_read_ecdh_pubkey(GNUTLS_ECC_CURVE_X448, der, dersize, params); + break; case GNUTLS_PK_GOST_01: case GNUTLS_PK_GOST_12_256: case GNUTLS_PK_GOST_12_512: -- cgit v1.2.1 From c5b657bfc8e0291912c4ba50a8fa0f6e0082cb53 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 22:25:41 -0400 Subject: certtool: when making X25519 or X448 certs, always use "key agreement" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is related to #1227 -- but in this case, it's enforcing a requirement of RFC 8410 ยง5. Signed-off-by: Daniel Kahn Gillmor --- src/certtool.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/certtool.c b/src/certtool.c index 1e0814a51f..825a306bc9 100644 --- a/src/certtool.c +++ b/src/certtool.c @@ -566,6 +566,10 @@ generate_certificate(gnutls_privkey_t * ret_key, if (result) usage |= GNUTLS_KEY_KEY_ENCIPHERMENT; + } else if (pk == GNUTLS_PK_ECDH_X25519 || + pk == GNUTLS_PK_ECDH_X448) { + /* X25519 and X448 are only for key agreement. */ + usage |= GNUTLS_KEY_KEY_AGREEMENT; } else { usage |= GNUTLS_KEY_DIGITAL_SIGNATURE; } -- cgit v1.2.1 From 838fc8c6d421113bda9eb8f284a35dedf4bff6f7 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 7 May 2021 20:14:53 -0400 Subject: certtool: add x448 and x25519 for --key-type This is a simple extension of the certtool command-line interface. Signed-off-by: Daniel Kahn Gillmor --- src/certtool-args.def | 2 +- src/certtool-common.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/certtool-args.def b/src/certtool-args.def index 5eef8e7110..61dcb712a5 100644 --- a/src/certtool-args.def +++ b/src/certtool-args.def @@ -200,7 +200,7 @@ flag = { arg-type = string; descrip = "Specify the key type to use on key generation"; doc = "This option can be combined with --generate-privkey, to specify -the key type to be generated. Valid options are, 'rsa', 'rsa-pss', 'dsa', 'ecdsa', 'ed25519, and 'ed448'.'. +the key type to be generated. Valid options are, 'rsa', 'rsa-pss', 'dsa', 'ecdsa', 'ed25519, 'ed448', 'x25519', and 'x448'.'. When combined with certificate generation it can be used to specify an RSA-PSS certificate when an RSA key is given."; }; diff --git a/src/certtool-common.c b/src/certtool-common.c index dba89df9a0..3fd63ab7aa 100644 --- a/src/certtool-common.c +++ b/src/certtool-common.c @@ -1647,6 +1647,10 @@ gnutls_pk_algorithm_t figure_key_type(const char *key_type) return GNUTLS_PK_EDDSA_ED25519; else if (strcasecmp(key_type, "ed448") == 0) return GNUTLS_PK_EDDSA_ED448; + else if (strcasecmp(key_type, "x25519") == 0) + return GNUTLS_PK_ECDH_X25519; + else if (strcasecmp(key_type, "x448") == 0) + return GNUTLS_PK_ECDH_X448; else if (strcasecmp(key_type, "dsa") == 0) return GNUTLS_PK_DSA; else if (strcasecmp(key_type, "ecdsa") == 0 || strcasecmp(key_type, "ecc") == 0) -- cgit v1.2.1 From d9ee30970e6a213b5e2185492ead0d1259e41ace Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 14 May 2021 17:14:41 -0400 Subject: tests: update details about sample X25519 certificate Signed-off-by: Daniel Kahn Gillmor --- tests/cert-tests/data/cert-eddsa.pem | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/cert-tests/data/cert-eddsa.pem b/tests/cert-tests/data/cert-eddsa.pem index b402a97042..dd328011a0 100644 --- a/tests/cert-tests/data/cert-eddsa.pem +++ b/tests/cert-tests/data/cert-eddsa.pem @@ -7,6 +7,11 @@ X.509 Certificate Information: Not After: Mon Dec 31 23:59:59 UTC 2040 Subject: CN=IETF Test Demo Subject Public Key Algorithm: ECDH (X25519) + Algorithm Security Level: High (256 bits) + Curve: X25519 + X: + 85:20:f0:09:89:30:a7:54:74:8b:7d:dc:b4:3e:f7:5a + 0d:bf:3a:0d:26:38:1a:f4:eb:a4:a9:8e:aa:9b:4e:6a Extensions: Basic Constraints (critical): Certificate Authority (CA): FALSE @@ -24,6 +29,11 @@ Other Information: Fingerprint: sha1:8b011a41d9b72f9848b1dcbd3a038fa8c9d0a536 sha256:180516f0a03e4893d234a28f3ad28921bc35d1b12bd35134847240dafb715a11 + Public Key ID: + sha1:fa752c35cb9a51d2069147e6e682155aaf2e5836 + sha256:291c5293e030452a599851a7c7298f3f16c3ff1bdfafcb598927f2631f9fa641 + Public Key PIN: + pin-sha256:KRxSk+AwRSpZmFGnxymPPxbD/xvfr8tZiSfyYx+fpkE= -----BEGIN CERTIFICATE----- MIIBLDCB36ADAgECAghWAUdKKo3DMDAFBgMrZXAwGTEXMBUGA1UEAwwOSUVURiBU -- cgit v1.2.1 From 30f3e456621a42ad38c5f153702af611e4a02625 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 11 Aug 2021 15:59:21 -0400 Subject: tests: add test for generating x25519 and x448 certificates These certs should work just fine for the purposes of cryptographic e-mail (S/MIME). These usage flags are also used in the end-entity certificates found in https://datatracker.ietf.org/doc/draft-ietf-lamps-samples/ Signed-off-by: Daniel Kahn Gillmor --- tests/cert-tests/Makefile.am | 2 +- tests/cert-tests/x25519-and-x448.sh | 101 ++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) create mode 100755 tests/cert-tests/x25519-and-x448.sh diff --git a/tests/cert-tests/Makefile.am b/tests/cert-tests/Makefile.am index 58041a9567..ef5f9029dd 100644 --- a/tests/cert-tests/Makefile.am +++ b/tests/cert-tests/Makefile.am @@ -112,7 +112,7 @@ dist_check_SCRIPTS = pathlen.sh aki.sh invalid-sig.sh email.sh \ pkcs12.sh certtool-crl-decoding.sh pkcs12-encode.sh pkcs12-corner-cases.sh inhibit-anypolicy.sh \ smime.sh cert-time.sh alt-chain.sh pkcs7-list-sign.sh pkcs7-eddsa.sh certtool-ecdsa.sh \ key-id.sh pkcs8.sh pkcs8-decode.sh ecdsa.sh illegal-rsa.sh pkcs8-invalid.sh key-invalid.sh \ - pkcs8-eddsa.sh certtool-subca.sh certtool-verify-profiles.sh x509-duplicate-ext.sh + pkcs8-eddsa.sh certtool-subca.sh certtool-verify-profiles.sh x509-duplicate-ext.sh x25519-and-x448.sh dist_check_SCRIPTS += key-id.sh ecdsa.sh pkcs8-invalid.sh key-invalid.sh pkcs8-decode.sh pkcs8.sh pkcs8-eddsa.sh \ certtool-utf8.sh crq.sh diff --git a/tests/cert-tests/x25519-and-x448.sh b/tests/cert-tests/x25519-and-x448.sh new file mode 100755 index 0000000000..23fbd5f1ca --- /dev/null +++ b/tests/cert-tests/x25519-and-x448.sh @@ -0,0 +1,101 @@ +#!/bin/sh + +# Copyright (C) 2021 Free Software Foundation, Inc. +# +# Author: Daniel Kahn Gillmor +# +# This file is part of GnuTLS. +# +# GnuTLS is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at +# your option) any later version. +# +# GnuTLS is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GnuTLS; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#set -e + +: ${srcdir=.} +: ${CERTTOOL=../../src/certtool${EXEEXT}} +TMPFILE=crfg-kx.$$.tmp +TMPCA=eddsa-ca.$$.tmp +TMPCAKEY=eddsa-ca-key.$$.tmp +TMPSUBCA=eddsa-subca.$$.tmp +TMPSUBCAKEY=eddsa-subca-key.$$.tmp +TMPKEY=kx-key.$$.tmp +TMPTEMPL=template.$$.tmp +TMPUSER=user.$$.tmp +VERIFYOUT=verify.$$.tmp + +if ! test -x "${CERTTOOL}"; then + exit 77 +fi + +for curve in 25519 448; do + echo ca > $TMPTEMPL + echo "cn = Ed$curve CA" >> $TMPTEMPL + + "${CERTTOOL}" --generate-privkey --key-type=ed$curve > $TMPCAKEY 2>/dev/null + + "${CERTTOOL}" -d 2 --generate-self-signed --template $TMPTEMPL \ + --load-privkey $TMPCAKEY \ + --outfile $TMPCA >$TMPFILE 2>&1 + + if [ $? != 0 ]; then + cat $TMPFILE + exit 1 + fi + + echo ca > $TMPTEMPL + echo "cn = Ed$curve Mid CA" >> $TMPTEMPL + + "${CERTTOOL}" --generate-privkey --key-type=ed$curve > $TMPSUBCAKEY 2>/dev/null + + "${CERTTOOL}" -d 2 --generate-certificate --template $TMPTEMPL \ + --load-ca-privkey $TMPCAKEY \ + --load-ca-certificate $TMPCA \ + --load-privkey $TMPSUBCAKEY \ + --outfile $TMPSUBCA >$TMPFILE 2>&1 + + if [ $? != 0 ]; then + cat $TMPFILE + exit 1 + fi + + echo "cn = End-user" > $TMPTEMPL + echo email_protection_key >> $TMPTEMPL + echo encryption_key >> $TMPTEMPL + + "${CERTTOOL}" --generate-privkey --key-type=x$curve > $TMPKEY 2>/dev/null + + "${CERTTOOL}" -d 2 --generate-certificate --template $TMPTEMPL \ + --load-ca-privkey $TMPSUBCAKEY \ + --load-ca-certificate $TMPSUBCA \ + --load-privkey $TMPKEY \ + --outfile $TMPUSER >$TMPFILE 2>&1 + + if [ $? != 0 ]; then + cat $TMPFILE + exit 1 + fi + + cat $TMPUSER $TMPSUBCA $TMPCA > $TMPFILE + "${CERTTOOL}" --verify-chain <$TMPFILE > $VERIFYOUT + + if [ $? != 0 ]; then + cat $VERIFYOUT + exit 1 + fi + + rm -f $VERIFYOUT $TMPUSER $TMPCA $TMPSUBCA $TMPTEMPL $TMPFILE + rm -f $TMPSUBCAKEY $TMPCAKEY $TMPKEY +done + +exit 0 -- cgit v1.2.1 From 78449e86c22dbad71c634cb18fb264a37d479e32 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Wed, 11 Aug 2021 17:31:40 -0400 Subject: NEWS: added news about certtool handling x448 and x25519 Signed-off-by: Daniel Kahn Gillmor --- NEWS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS b/NEWS index 0fcd043aa3..0ae6410098 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,9 @@ See the end for copying conditions. * Version 3.7.3 (unreleased) +** certtool: Certtool can now generate, manipulate, and evaluate x25519 and + x448 public keys, private keys, and certificates. + ** API and ABI modifications: GNUTLS_PRIVKEY_FLAG_RSA_PSS_FIXED_SALT_LENGTH: new flag in gnutls_privkey_flags_t GNUTLS_VERIFY_RSA_PSS_FIXED_SALT_LENGTH: new flag in gnutls_certificate_verify_flags -- cgit v1.2.1 From a00a79ddf41eb14d56bdea076b5c252029896431 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Fri, 17 Sep 2021 16:37:24 -0400 Subject: lib/x509: Avoid memcpy when string is empty This fixes an ASAN warning in fuzz/gnutls_private_key_parser_fuzzer when run against the malformed private key fuzz/gnutls_private_key_parser_fuzzer.in/10a5c92fa30ddb6cbb4286d7699b2b7a7e032b17 Signed-off-by: Daniel Kahn Gillmor --- lib/x509/common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/x509/common.c b/lib/x509/common.c index c156bd96a9..94d206ff75 100644 --- a/lib/x509/common.c +++ b/lib/x509/common.c @@ -630,7 +630,8 @@ _gnutls_x509_decode_string(unsigned int etype, if (td.data == NULL) return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); - memcpy(td.data, str, str_size); + if (str_size > 0) + memcpy(td.data, str, str_size); td.data[str_size] = 0; if (allow_ber) -- cgit v1.2.1