diff options
-rw-r--r-- | lib/auth/cert.c | 254 | ||||
-rw-r--r-- | lib/auth/cert.h | 5 | ||||
-rw-r--r-- | lib/cert.c | 284 | ||||
-rw-r--r-- | lib/ext/post_handshake.c | 2 | ||||
-rw-r--r-- | lib/gnutls_int.h | 10 | ||||
-rw-r--r-- | lib/includes/gnutls/abstract.h | 33 | ||||
-rw-r--r-- | lib/libgnutls.map | 1 |
7 files changed, 366 insertions, 223 deletions
diff --git a/lib/auth/cert.c b/lib/auth/cert.c index 2d02465089..085857bcd2 100644 --- a/lib/auth/cert.c +++ b/lib/auth/cert.c @@ -47,19 +47,10 @@ #include "abstract_int.h" #include "debug.h" -static gnutls_pcert_st *alloc_and_load_x509_certs(gnutls_x509_crt_t * - certs, unsigned); -static gnutls_privkey_t alloc_and_load_x509_key(gnutls_x509_privkey_t key, - int deinit); - -#ifdef ENABLE_PKCS11 -static gnutls_privkey_t alloc_and_load_pkcs11_key(gnutls_pkcs11_privkey_t - key, int deinit); -#endif - static void _gnutls_selected_certs_set(gnutls_session_t session, gnutls_pcert_st * certs, int ncerts, + gnutls_datum_t *ocsp, int nocsp, gnutls_privkey_t key, int need_free, gnutls_status_request_ocsp_func ocsp_func, void *ocsp_func_ptr); @@ -356,14 +347,13 @@ call_get_cert_callback(gnutls_session_t session, int issuers_dn_length, gnutls_pk_algorithm_t * pk_algos, int pk_algos_length) { - unsigned i; - gnutls_pcert_st *local_certs = NULL; gnutls_privkey_t local_key = NULL; int ret = GNUTLS_E_INTERNAL_ERROR; gnutls_certificate_type_t type = gnutls_certificate_type_get(session); gnutls_certificate_credentials_t cred; - gnutls_retr2_st st2; gnutls_pcert_st *pcert = NULL; + gnutls_datum_t *ocsp = NULL; + unsigned int ocsp_length = 0; unsigned int pcert_length = 0; cred = (gnutls_certificate_credentials_t) @@ -373,15 +363,23 @@ call_get_cert_callback(gnutls_session_t session, return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - memset(&st2, 0, sizeof(st2)); + if (cred->get_cert_callback3) { + struct gnutls_cert_retr_st info; + unsigned int flags = 0; + + memset(&info, 0, sizeof(info)); + info.req_ca_rdn = issuers_dn; + info.nreqs = issuers_dn_length; + info.pk_algos = pk_algos; + info.pk_algos_length = pk_algos_length; + info.cred = cred; - if (cred->get_cert_callback2) { /* we avoid all allocations and transformations */ ret = - cred->get_cert_callback2(session, issuers_dn, - issuers_dn_length, pk_algos, - pk_algos_length, &pcert, - &pcert_length, &local_key); + cred->get_cert_callback3(session, &info, + &pcert, &pcert_length, + &ocsp, &ocsp_length, + &local_key, &flags); if (ret < 0) return gnutls_assert_val(GNUTLS_E_USER_ERROR); @@ -394,100 +392,15 @@ call_get_cert_callback(gnutls_session_t session, } _gnutls_selected_certs_set(session, pcert, pcert_length, - local_key, 0, NULL, NULL); + ocsp, ocsp_length, + local_key, flags&GNUTLS_CERT_RETR_DEINIT_ALL?1:0, + NULL, NULL); return 0; - - } else if (cred->get_cert_callback) { - ret = - cred->get_cert_callback(session, issuers_dn, - issuers_dn_length, pk_algos, - pk_algos_length, &st2); - - } else { /* compatibility mode */ + } else { gnutls_assert(); return GNUTLS_E_INTERNAL_ERROR; } - - if (ret < 0) { - gnutls_assert(); - return GNUTLS_E_USER_ERROR; - } - - if (st2.ncerts == 0) - return 0; /* no certificate was selected */ - - if (type != st2.cert_type) { - gnutls_assert(); - ret = GNUTLS_E_INVALID_REQUEST; - goto cleanup; - } - - if (type == GNUTLS_CRT_X509) { - local_certs = - alloc_and_load_x509_certs(st2.cert.x509, st2.ncerts); - } else { /* PGP */ - ret = GNUTLS_E_UNIMPLEMENTED_FEATURE; - goto cleanup; - } - - if (local_certs == NULL) { - gnutls_assert(); - ret = GNUTLS_E_MEMORY_ERROR; - goto cleanup; - } - - switch (st2.key_type) { - case GNUTLS_PRIVKEY_PKCS11: -#ifdef ENABLE_PKCS11 - if (st2.key.pkcs11 != NULL) { - local_key = - alloc_and_load_pkcs11_key(st2.key.pkcs11, - st2.deinit_all); - if (local_key == NULL) { - gnutls_assert(); - ret = GNUTLS_E_INTERNAL_ERROR; - goto cleanup; - } - } -#endif - break; - case GNUTLS_PRIVKEY_X509: - if (st2.key.x509 != NULL) { - local_key = - alloc_and_load_x509_key(st2.key.x509, - st2.deinit_all); - if (local_key == NULL) { - gnutls_assert(); - ret = GNUTLS_E_INTERNAL_ERROR; - goto cleanup; - } - } - break; - default: - gnutls_assert(); - ret = GNUTLS_E_INVALID_REQUEST; - goto cleanup; - } - - _gnutls_selected_certs_set(session, local_certs, - st2.ncerts, local_key, 1, - NULL, NULL); - - ret = 0; - - cleanup: - - if (st2.cert_type == GNUTLS_CRT_X509) { - if (st2.deinit_all) { - for (i = 0; i < st2.ncerts; i++) { - gnutls_x509_crt_deinit(st2.cert.x509[i]); - } - gnutls_free(st2.cert.x509); - } - } - - return ret; } /* Finds the appropriate certificate depending on the cA Distinguished name @@ -517,8 +430,7 @@ _gnutls_select_client_cert(gnutls_session_t session, return GNUTLS_E_INSUFFICIENT_CREDENTIALS; } - if (cred->get_cert_callback != NULL - || cred->get_cert_callback2 != NULL) { + if (cred->get_cert_callback3 != NULL) { /* use a callback to get certificate */ @@ -579,11 +491,12 @@ _gnutls_select_client_cert(gnutls_session_t session, cert_list[0], cred->certs[indx]. cert_list_length, + NULL, 0, cred->certs[indx].pkey, 0, NULL, NULL); } else { _gnutls_selected_certs_set(session, NULL, 0, NULL, 0, - NULL, NULL); + NULL, 0, NULL, NULL); } result = 0; @@ -1225,109 +1138,6 @@ _gnutls_get_selected_cert(gnutls_session_t session, return 0; } -/* converts the given x509 certificate list to gnutls_pcert_st* and allocates - * space for them. - */ -static gnutls_pcert_st *alloc_and_load_x509_certs(gnutls_x509_crt_t * - certs, unsigned ncerts) -{ - gnutls_pcert_st *local_certs; - int ret = 0; - unsigned i, j; - - if (certs == NULL) - return NULL; - - local_certs = gnutls_malloc(sizeof(gnutls_pcert_st) * ncerts); - if (local_certs == NULL) { - gnutls_assert(); - return NULL; - } - - for (i = 0; i < ncerts; i++) { - ret = gnutls_pcert_import_x509(&local_certs[i], certs[i], 0); - if (ret < 0) - break; - } - - if (ret < 0) { - gnutls_assert(); - for (j = 0; j < i; j++) { - gnutls_pcert_deinit(&local_certs[j]); - } - gnutls_free(local_certs); - return NULL; - } - - return local_certs; -} - -/* converts the given x509 key to gnutls_privkey* and allocates - * space for it. - */ -static gnutls_privkey_t -alloc_and_load_x509_key(gnutls_x509_privkey_t key, int deinit) -{ - gnutls_privkey_t local_key; - int ret = 0; - - if (key == NULL) - return NULL; - - ret = gnutls_privkey_init(&local_key); - if (ret < 0) { - gnutls_assert(); - return NULL; - } - - ret = - gnutls_privkey_import_x509(local_key, key, - deinit ? - GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE : 0); - if (ret < 0) { - gnutls_assert(); - gnutls_privkey_deinit(local_key); - return NULL; - } - - return local_key; -} - -#ifdef ENABLE_PKCS11 - -/* converts the given raw key to gnutls_privkey* and allocates - * space for it. - */ -static gnutls_privkey_t -alloc_and_load_pkcs11_key(gnutls_pkcs11_privkey_t key, int deinit) -{ - gnutls_privkey_t local_key; - int ret = 0; - - if (key == NULL) - return NULL; - - ret = gnutls_privkey_init(&local_key); - if (ret < 0) { - gnutls_assert(); - return NULL; - } - - ret = - gnutls_privkey_import_pkcs11(local_key, key, - deinit ? - GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE - : 0); - if (ret < 0) { - gnutls_assert(); - gnutls_privkey_deinit(local_key); - return NULL; - } - - return local_key; -} - -#endif void _gnutls_selected_certs_deinit(gnutls_session_t session) { @@ -1341,6 +1151,13 @@ void _gnutls_selected_certs_deinit(gnutls_session_t session) } gnutls_free(session->internals.selected_cert_list); + for (i = 0; + i < session->internals.selected_ocsp_length; i++) { + _gnutls_free_datum(&session->internals. + selected_ocsp[i]); + } + gnutls_free(session->internals.selected_ocsp); + gnutls_privkey_deinit(session->internals.selected_key); } session->internals.selected_ocsp_func = NULL; @@ -1356,6 +1173,7 @@ void _gnutls_selected_certs_deinit(gnutls_session_t session) static void _gnutls_selected_certs_set(gnutls_session_t session, gnutls_pcert_st * certs, int ncerts, + gnutls_datum_t *ocsp, int nocsp, gnutls_privkey_t key, int need_free, gnutls_status_request_ocsp_func ocsp_func, void *ocsp_func_ptr) @@ -1364,6 +1182,10 @@ _gnutls_selected_certs_set(gnutls_session_t session, session->internals.selected_cert_list = certs; session->internals.selected_cert_list_length = ncerts; + + session->internals.selected_ocsp = ocsp; + session->internals.selected_ocsp_length = nocsp; + session->internals.selected_key = key; session->internals.selected_need_free = need_free; @@ -1486,8 +1308,7 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e /* If the callback which retrieves certificate has been set, * use it and leave. We make sure that this is called once. */ - if (cred->get_cert_callback - || cred->get_cert_callback2) { + if (cred->get_cert_callback3) { if (session->internals.selected_cert_list_length == 0) { ret = call_get_cert_callback(session, NULL, 0, NULL, 0); @@ -1608,6 +1429,7 @@ _gnutls_server_select_cert(gnutls_session_t session, const gnutls_cipher_suite_e _gnutls_selected_certs_set(session, &cred->certs[idx].cert_list[0], cred->certs[idx].cert_list_length, + NULL, 0, cred->certs[idx].pkey, 0, cred->certs[idx].ocsp_func, cred->certs[idx].ocsp_func_ptr); diff --git a/lib/auth/cert.h b/lib/auth/cert.h index fee7c03ccd..f890f48624 100644 --- a/lib/auth/cert.h +++ b/lib/auth/cert.h @@ -77,8 +77,9 @@ typedef struct gnutls_certificate_credentials_st { /* It's a mess here. However we need to keep the old 3 functions * for compatibility */ - gnutls_certificate_retrieve_function *get_cert_callback; /* deprecated */ - gnutls_certificate_retrieve_function2 *get_cert_callback2; + gnutls_certificate_retrieve_function *legacy_cert_cb1; /* deprecated */ + gnutls_certificate_retrieve_function2 *legacy_cert_cb2; + gnutls_certificate_retrieve_function3 *get_cert_callback3; gnutls_certificate_verify_function *verify_callback; diff --git a/lib/cert.c b/lib/cert.c index 9e3a00f484..617ad8e644 100644 --- a/lib/cert.c +++ b/lib/cert.c @@ -236,6 +236,110 @@ gnutls_certificate_allocate_credentials(gnutls_certificate_credentials_t * return 0; } +/* converts the given x509 certificate list to gnutls_pcert_st* and allocates + * space for them. + */ +static gnutls_pcert_st *alloc_and_load_x509_certs(gnutls_x509_crt_t * + certs, unsigned ncerts) +{ + gnutls_pcert_st *local_certs; + int ret = 0; + unsigned i, j; + + if (certs == NULL) + return NULL; + + local_certs = gnutls_malloc(sizeof(gnutls_pcert_st) * ncerts); + if (local_certs == NULL) { + gnutls_assert(); + return NULL; + } + + for (i = 0; i < ncerts; i++) { + ret = gnutls_pcert_import_x509(&local_certs[i], certs[i], 0); + if (ret < 0) + break; + } + + if (ret < 0) { + gnutls_assert(); + for (j = 0; j < i; j++) { + gnutls_pcert_deinit(&local_certs[j]); + } + gnutls_free(local_certs); + return NULL; + } + + return local_certs; +} + +/* converts the given x509 key to gnutls_privkey* and allocates + * space for it. + */ +static gnutls_privkey_t +alloc_and_load_x509_key(gnutls_x509_privkey_t key, int deinit) +{ + gnutls_privkey_t local_key; + int ret = 0; + + if (key == NULL) + return NULL; + + ret = gnutls_privkey_init(&local_key); + if (ret < 0) { + gnutls_assert(); + return NULL; + } + + ret = + gnutls_privkey_import_x509(local_key, key, + deinit ? + GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE : 0); + if (ret < 0) { + gnutls_assert(); + gnutls_privkey_deinit(local_key); + return NULL; + } + + return local_key; +} + +#ifdef ENABLE_PKCS11 + +/* converts the given raw key to gnutls_privkey* and allocates + * space for it. + */ +static gnutls_privkey_t +alloc_and_load_pkcs11_key(gnutls_pkcs11_privkey_t key, int deinit) +{ + gnutls_privkey_t local_key; + int ret = 0; + + if (key == NULL) + return NULL; + + ret = gnutls_privkey_init(&local_key); + if (ret < 0) { + gnutls_assert(); + return NULL; + } + + ret = + gnutls_privkey_import_pkcs11(local_key, key, + deinit ? + GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE + : 0); + if (ret < 0) { + gnutls_assert(); + gnutls_privkey_deinit(local_key); + return NULL; + } + + return local_key; +} + +#endif + /** * gnutls_certificate_server_set_request: * @session: is a #gnutls_session_t type. @@ -254,6 +358,102 @@ gnutls_certificate_server_set_request(gnutls_session_t session, session->internals.send_cert_req = req; } +static int call_legacy_cert_cb1(gnutls_session_t session, + const struct gnutls_cert_retr_st *info, + gnutls_pcert_st **certs, + unsigned int *pcert_length, + gnutls_datum_t **ocsp, + unsigned int *ocsp_length, + gnutls_privkey_t *privkey, + unsigned int *flags) +{ + gnutls_retr2_st st2; + gnutls_pcert_st *local_certs = NULL; + gnutls_privkey_t local_key = NULL; + unsigned i; + int ret; + + *ocsp_length = 0; + + memset(&st2, 0, sizeof(st2)); + + ret = info->cred->legacy_cert_cb1(session, info->req_ca_rdn, info->nreqs, + info->pk_algos, info->pk_algos_length, + &st2); + if (ret < 0) + return gnutls_assert_val(ret); + + if (st2.cert_type != GNUTLS_CRT_X509) { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + local_certs = + alloc_and_load_x509_certs(st2.cert.x509, st2.ncerts); + if (local_certs == NULL) { + gnutls_assert(); + ret = GNUTLS_E_MEMORY_ERROR; + goto cleanup; + } + + switch (st2.key_type) { +#ifdef ENABLE_PKCS11 + case GNUTLS_PRIVKEY_PKCS11: + if (st2.key.pkcs11 != NULL) { + local_key = + alloc_and_load_pkcs11_key(st2.key.pkcs11, + st2.deinit_all); + if (local_key == NULL) { + gnutls_assert(); + ret = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + } + break; +#endif + case GNUTLS_PRIVKEY_X509: + if (st2.key.x509 != NULL) { + local_key = + alloc_and_load_x509_key(st2.key.x509, + st2.deinit_all); + if (local_key == NULL) { + gnutls_assert(); + ret = GNUTLS_E_INTERNAL_ERROR; + goto cleanup; + } + } + break; + default: + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + *privkey = local_key; + *certs = local_certs; + *pcert_length = st2.ncerts; + + /* flag the caller to deinitialize our values */ + *flags |= GNUTLS_CERT_RETR_DEINIT_ALL; + + ret = 0; + + cleanup: + + if (st2.cert_type == GNUTLS_CRT_X509) { + if (st2.deinit_all) { + for (i = 0; i < st2.ncerts; i++) { + gnutls_x509_crt_deinit(st2.cert.x509[i]); + } + gnutls_free(st2.cert.x509); + } + } + + return ret; + +} + /** * gnutls_certificate_set_retrieve_function: * @cred: is a #gnutls_certificate_credentials_t type. @@ -298,7 +498,30 @@ void gnutls_certificate_set_retrieve_function (gnutls_certificate_credentials_t cred, gnutls_certificate_retrieve_function * func) { - cred->get_cert_callback = func; + cred->legacy_cert_cb1 = func; + cred->get_cert_callback3 = call_legacy_cert_cb1; +} + +static int call_legacy_cert_cb2(gnutls_session_t session, + const struct gnutls_cert_retr_st *info, + gnutls_pcert_st **certs, + unsigned int *pcert_length, + gnutls_datum_t **ocsp, + unsigned int *ocsp_length, + gnutls_privkey_t *privkey, + unsigned int *flags) +{ + int ret; + *ocsp_length = 0; + /* flags will be assumed to be zero */ + + ret = info->cred->legacy_cert_cb2(session, info->req_ca_rdn, info->nreqs, + info->pk_algos, info->pk_algos_length, + certs, pcert_length, privkey); + if (ret < 0) { + gnutls_assert(); + } + return ret; } /** @@ -350,7 +573,64 @@ void gnutls_certificate_set_retrieve_function2 (gnutls_certificate_credentials_t cred, gnutls_certificate_retrieve_function2 * func) { - cred->get_cert_callback2 = func; + cred->legacy_cert_cb2 = func; + cred->get_cert_callback3 = call_legacy_cert_cb2; +} + +/** + * gnutls_certificate_set_retrieve_function3: + * @cred: is a #gnutls_certificate_credentials_t type. + * @func: is the callback function + * + * This function sets a callback to be called in order to retrieve the + * certificate and OCSP responses to be used in the handshake. The callback will + * take control only if a certificate is requested by the peer. + * + * The callback's function prototype is defined in `abstract.h': + * int (*callback)(gnutls_session_t, const struct gnutls_cert_retr_st *info, + * gnutls_pcert_st **certs, unsigned int *pcert_length, + * gnutls_datum_t **ocsp, unsigned int *ocsp_length, + * gnutls_privkey_t * pkey, unsigned int *flags); + * + * The info field of the callback contains: + * @req_ca_dn which is a list with the CA names that the server considers trusted. + * This is a hint and typically the client should send a certificate that is signed + * by one of these CAs. These names, when available, are DER encoded. To get a more + * meaningful value use the function gnutls_x509_rdn_get(). + * @pk_algos contains a list with server's acceptable public key algorithms. + * The certificate returned should support the server's given algorithms. + * + * The callback should fill-in the following values. + * + * @pcert should contain a single certificate and public key or a list of them. + * @pcert_length is the size of the previous list. + * @ocsp should contain a single OCSP response or a list of them. + * @ocsp_length is the size of the previous list. + * @pkey is the private key. + * + * If the callback function is provided then gnutls will call it, during + * handshake, after the certificate request message has been received, + * or during post-handshake. + * + * All the provided by the callback values will not be released or + * modified by gnutls. + * + * When this callback is set in server side, @pk_algos and @req_ca_dn are NULL. + * + * The callback function should set the certificate and OCSP response + * list to be sent, and return 0 on success. If no certificate was selected then + * the @pcert_length and @Ocsp_length should be set to zero. The return + * value (-1) indicates error and the handshake will be terminated. If both + * certificates are set in the credentials and a callback is available, the + * callback takes predence. + * + * Since: 3.6.xx + **/ +void gnutls_certificate_set_retrieve_function3 + (gnutls_certificate_credentials_t cred, + gnutls_certificate_retrieve_function3 *func) +{ + cred->get_cert_callback3 = func; } /** diff --git a/lib/ext/post_handshake.c b/lib/ext/post_handshake.c index 9aa67e2aed..0af7e53f21 100644 --- a/lib/ext/post_handshake.c +++ b/lib/ext/post_handshake.c @@ -94,7 +94,7 @@ _gnutls_post_handshake_send_params(gnutls_session_t session, if (unlikely(max == NULL)) return gnutls_assert_val(0); - if (max->post_handshake_auth && (cred->ncerts || cred->get_cert_callback2 || cred->get_cert_callback)) + if (max->post_handshake_auth && (cred->ncerts || cred->get_cert_callback3)) return GNUTLS_E_INT_RET_0; else return 0; diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index bb422ddbe9..63111a3a89 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -1085,9 +1085,17 @@ typedef struct { gnutls_pcert_st *selected_cert_list; int16_t selected_cert_list_length; struct gnutls_privkey_st *selected_key; - bool selected_need_free; + + /* new callbacks such as gnutls_certificate_retrieve_function3 + * set the selected_ocsp datum values. The older OCSP callback-based + * functions, set the ocsp_func. The former takes precedence when + * set. + */ + gnutls_datum_t *selected_ocsp; + int16_t selected_ocsp_length; gnutls_status_request_ocsp_func selected_ocsp_func; void *selected_ocsp_func_ptr; + bool selected_need_free; /* This holds the default version that our first diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index bf29f877cd..bc97086262 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -655,7 +655,38 @@ typedef int gnutls_certificate_retrieve_function2(gnutls_session_t, void gnutls_certificate_set_retrieve_function2 (gnutls_certificate_credentials_t cred, - gnutls_certificate_retrieve_function2 * func); + gnutls_certificate_retrieve_function2 *func); + +struct gnutls_cert_retr_st { + unsigned version; /* set to 1 */ + gnutls_certificate_credentials_t cred; + const gnutls_datum_t *req_ca_rdn; + unsigned nreqs; + const gnutls_pk_algorithm_t *pk_algos; + unsigned pk_algos_length; + + /* other fields may be added if version is > 1 */ + unsigned char padding[64]; +}; + +/* When the callback sets this value, gnutls will deinitialize the given + * values after use */ +#define GNUTLS_CERT_RETR_DEINIT_ALL 1 + +typedef int gnutls_certificate_retrieve_function3( + gnutls_session_t, + const struct gnutls_cert_retr_st *info, + gnutls_pcert_st **certs, + unsigned int *pcert_length, + gnutls_datum_t **ocsp, + unsigned int *ocsp_length, + gnutls_privkey_t *privkey, + unsigned int *flags); + + +void gnutls_certificate_set_retrieve_function3 + (gnutls_certificate_credentials_t cred, + gnutls_certificate_retrieve_function3 *func); int gnutls_certificate_set_key(gnutls_certificate_credentials_t res, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index d29d650a81..e861aeaf7d 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1214,6 +1214,7 @@ GNUTLS_3_6_xx gnutls_ocsp_resp_import2; gnutls_ocsp_resp_export2; gnutls_ocsp_resp_list_import2; + gnutls_certificate_retrieve_function3; } GNUTLS_3_6_2; GNUTLS_FIPS140_3_4 { |