From e680ca2c40cb4d7b1b487a3fab2debdce2415afa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoren=20Gr=C3=BCttemeier?= Date: Thu, 12 Sep 2024 15:19:37 +0200 Subject: [PATCH 1/7] tcp assembling --- Wireshark/plugins/v2gtp.lua | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Wireshark/plugins/v2gtp.lua b/Wireshark/plugins/v2gtp.lua index ce676bd..dab8ea0 100644 --- a/Wireshark/plugins/v2gtp.lua +++ b/Wireshark/plugins/v2gtp.lua @@ -30,6 +30,10 @@ p_v2gtp.prefs["versioninfo"] = Pref.statictext("Version " .. v2gcommon.DS_V2GSHA local V2GTP_HDR_LENGTH = 8 +local function get_v2gtp_length(buf, pktinfo, offset) + return buf(offset + 4,4):uint() + V2GTP_HDR_LENGTH +end + local f_pv = ProtoField.uint8("v2gtp.protoversion", "Protocol Version", base.HEX) local f_ipv = ProtoField.uint8("v2gtp.inverseprotoversion", "Inverse Protocol Version", base.HEX) local f_pt = ProtoField.uint16("v2gtp.payloadtype", "Payload Type", base.HEX) @@ -77,10 +81,6 @@ p_v2gtp.fields = {f_pv, f_ipv, f_pt, f_len} -- PDU dissection function local function v2gtp_pdu_dissect(buf, pinfo, root) - if tostring(buf(0, 2)) ~= "01fe" then - return 0 - end - local p_type_num = buf(2, 2):uint() local prev_proto = tostring(pinfo.cols.protocol) @@ -171,7 +171,17 @@ end -- main dissection function function p_v2gtp.dissector(buf, pinfo, root) - return v2gtp_pdu_dissect(buf, pinfo, root) + -- plausibility checks + if buf:len() < V2GTP_HDR_LENGTH then return 0 end + if tostring(buf(0, 2)) ~= "01fe" then return 0 end + + -- if above TCP we need to assemble the PDU + if pinfo.port_type == 2 then + dissect_tcp_pdus(buf, root, V2GTP_HDR_LENGTH, get_v2gtp_length, v2gtp_pdu_dissect) + return buf:len() + else + return v2gtp_pdu_dissect(buf, pinfo, root) + end end -- initialization routine From 7a99d3d1033f6b8f363e49c1c2352a3efa5d04a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoren=20Gr=C3=BCttemeier?= Date: Fri, 20 Sep 2024 13:17:02 +0200 Subject: [PATCH 2/7] improved certificate errors --- V2G_Libraries/v2gLib/src/CertHelper.h | 9 +- V2G_Libraries/v2gLib/src/GnuTLSHelper.cpp | 94 +++++++++++++----- V2G_Libraries/v2gLib/src/LuaLibV2G.cpp | 18 +++- Wireshark/plugins/v2gmsg.lua | 115 ++++++++++++---------- 4 files changed, 150 insertions(+), 86 deletions(-) diff --git a/V2G_Libraries/v2gLib/src/CertHelper.h b/V2G_Libraries/v2gLib/src/CertHelper.h index b16d139..7b1a09a 100644 --- a/V2G_Libraries/v2gLib/src/CertHelper.h +++ b/V2G_Libraries/v2gLib/src/CertHelper.h @@ -17,12 +17,12 @@ struct X509CertInfos { - bool valid = false; + int result = -1; // GnuTLS error code. Note: GNUTLS_E_SUCCESS == 0 std::string subject = "ERROR"; std::string issuer = "ERROR"; - int version = -1; // Note: this is defined by standards to be one less than - // the certificate version. E.g., version 3 certificate will return 2 - std::string serial_number = "ERROR"; // hex-string + int version = -1; // Note: this is defined by standards to be one less than + // the certificate version. E.g., version 3 certificate will return 2 + std::string serial_number = "ERROR"; // hex-string std::string sig_algorithm = "ERROR"; std::string sig_value = "ERROR"; std::string time_not_after = "ERROR"; @@ -40,3 +40,4 @@ struct X509CertInfos }; X509CertInfos get_cert_info(std::string x509_content); +std::string get_error_description(int error_code); \ No newline at end of file diff --git a/V2G_Libraries/v2gLib/src/GnuTLSHelper.cpp b/V2G_Libraries/v2gLib/src/GnuTLSHelper.cpp index b2985a0..9010738 100644 --- a/V2G_Libraries/v2gLib/src/GnuTLSHelper.cpp +++ b/V2G_Libraries/v2gLib/src/GnuTLSHelper.cpp @@ -25,47 +25,54 @@ constexpr int MAX_STRING_LEN = 512; +std::string get_error_description(int error_code) +{ + return std::string(gnutls_strerror(error_code)); +} + X509CertInfos get_cert_info(std::string x509_content) { X509CertInfos cert_info{}; gnutls_x509_crt_t cert; - if (gnutls_x509_crt_init(&cert) < 0) + cert_info.result = gnutls_x509_crt_init(&cert); + if (cert_info.result < 0) { fprintf(stderr, "dsV2G CertInfo error in initialization\n"); - cert_info.valid = false; return cert_info; } char buffer[MAX_STRING_LEN]; size_t size = sizeof(buffer); - cert_info.valid = true; - gnutls_datum_t cert_in; cert_in.data = new unsigned char[x509_content.length()]; - strcpy((char *)cert_in.data, x509_content.c_str()); + strncpy((char *)cert_in.data, x509_content.c_str(), x509_content.length()); cert_in.size = x509_content.length(); - if (gnutls_x509_crt_import(cert, &cert_in, GNUTLS_X509_FMT_PEM) < 0) + cert_info.result = gnutls_x509_crt_import(cert, &cert_in, GNUTLS_X509_FMT_PEM); + if (cert_info.result < 0) { fprintf(stderr, "dsV2G CertInfo error parsing certificate\n"); - cert_info.valid = false; delete[] cert_in.data; gnutls_x509_crt_deinit(cert); return cert_info; } size = sizeof(buffer); - if (gnutls_x509_crt_get_serial(cert, buffer, &size) != GNUTLS_E_SUCCESS) + int errn = gnutls_x509_crt_get_serial(cert, buffer, &size); + if (errn != GNUTLS_E_SUCCESS) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_serial\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = errn; + } cert_info.serial_number = "ERROR"; } else { - cert_info.serial_number = uint8_to_hex_string((uint8_t *)buffer, size); + cert_info.serial_number = "0x" + uint8_to_hex_string((uint8_t *)buffer, size); } time_t expiration_time, activation_time; @@ -73,25 +80,31 @@ X509CertInfos get_cert_info(std::string x509_content) if (expiration_time == -1) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_expiration_time\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = expiration_time; + } cert_info.time_not_after = "ERROR"; } else { cert_info.time_not_after = ctime(&expiration_time); - cert_info.time_not_after.pop_back(); // remove linebreak + cert_info.time_not_after.pop_back(); // remove linebreak } activation_time = gnutls_x509_crt_get_activation_time(cert); if (activation_time == -1) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_activation_time\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = activation_time; + } cert_info.time_not_before = "ERROR"; } else { cert_info.time_not_before = ctime(&activation_time); - cert_info.time_not_before.pop_back(); // remove linebreak + cert_info.time_not_before.pop_back(); // remove linebreak } int sig_algorithm = gnutls_x509_crt_get_signature_algorithm(cert); @@ -101,14 +114,21 @@ X509CertInfos get_cert_info(std::string x509_content) if (cert_info.version < 0) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_version\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = cert_info.version; + } } size = sizeof(buffer); - if (gnutls_x509_crt_get_dn(cert, buffer, &size) != GNUTLS_E_SUCCESS) + errn = gnutls_x509_crt_get_dn(cert, buffer, &size); + if (errn != GNUTLS_E_SUCCESS) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_dn\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = errn; + } cert_info.subject = "ERROR"; } else @@ -117,10 +137,14 @@ X509CertInfos get_cert_info(std::string x509_content) } size = sizeof(buffer); - if (gnutls_x509_crt_get_issuer_dn(cert, buffer, &size) != GNUTLS_E_SUCCESS) + errn = gnutls_x509_crt_get_issuer_dn(cert, buffer, &size); + if (errn != GNUTLS_E_SUCCESS) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_issuer_dn\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = errn; + } cert_info.issuer = "ERROR"; } else @@ -129,10 +153,14 @@ X509CertInfos get_cert_info(std::string x509_content) } size = sizeof(buffer); - if (gnutls_x509_crt_get_signature(cert, buffer, &size) != GNUTLS_E_SUCCESS) + errn = gnutls_x509_crt_get_signature(cert, buffer, &size); + if (errn != GNUTLS_E_SUCCESS) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_signature\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = errn; + } cert_info.sig_value = "ERROR"; } else @@ -145,7 +173,10 @@ X509CertInfos get_cert_info(std::string x509_content) if (pk_algo < 0) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_pk_algorithm\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = pk_algo; + } cert_info.pubkey_algorithm = "ERROR"; } else @@ -164,7 +195,10 @@ X509CertInfos get_cert_info(std::string x509_content) else if (basic_constraints_ret < GNUTLS_E_SUCCESS) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_basic_constraints %d\n", basic_constraints_ret); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = basic_constraints_ret; + } cert_info.v3ext_basic_constraint = "ERROR"; cert_info.v3ext_basic_constraint_CA = "ERROR"; } @@ -208,7 +242,10 @@ X509CertInfos get_cert_info(std::string x509_content) else if (key_usage_ret != GNUTLS_E_SUCCESS) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_key_usage\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = key_usage_ret; + } cert_info.v3ext_key_usage = "ERROR"; cert_info.v3ext_key_usage_critical = "ERROR"; } @@ -263,7 +300,10 @@ X509CertInfos get_cert_info(std::string x509_content) else if (keyIdRet != GNUTLS_E_SUCCESS) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_subject_key_id\n"); - cert_info.valid = false; + if (cert_info.result == GNUTLS_E_SUCCESS) + { + cert_info.result = keyIdRet; + } cert_info.v3ext_subjkey_id = "ERROR"; cert_info.v3ext_subjkey_id_critical = "ERROR"; } @@ -289,7 +329,7 @@ X509CertInfos get_cert_info(std::string x509_content) if (gnutls_x509_crt_get_pk_ecc_raw(cert, &curve, &coord_x, &coord_y) != GNUTLS_E_SUCCESS) { fprintf(stderr, "dsV2G CertInfo error gnutls_x509_crt_get_pk_ecc_raw\n"); - // cInfo.valid = false; // keep valid in case this is not ecc pk + // cert_info.result - keep previous result in case this is not ecc pk cert_info.spk_NIST_curve = "ERROR"; cert_info.spk_pub = "ERROR"; } @@ -304,4 +344,4 @@ X509CertInfos get_cert_info(std::string x509_content) gnutls_x509_crt_deinit(cert); return cert_info; -} \ No newline at end of file +} diff --git a/V2G_Libraries/v2gLib/src/LuaLibV2G.cpp b/V2G_Libraries/v2gLib/src/LuaLibV2G.cpp index b97e8fc..7955cb7 100644 --- a/V2G_Libraries/v2gLib/src/LuaLibV2G.cpp +++ b/V2G_Libraries/v2gLib/src/LuaLibV2G.cpp @@ -25,6 +25,15 @@ extern "C" #include "Decoder.h" #include "CertHelper.h" +static int l_getGnuTlsErrorDescription(lua_State *L) +{ + int error_code = luaL_checkinteger(L, 0); + + lua_pushstring(L, get_error_description(error_code).c_str()); + + return 1; +} + static int l_getX509Infos(lua_State *L) { size_t certSize; @@ -36,10 +45,10 @@ static int l_getX509Infos(lua_State *L) X509CertInfos cInfo = get_cert_info(fullcert); - lua_pushboolean(L, cInfo.valid); + lua_pushinteger(L, cInfo.result); lua_pushstring(L, cInfo.subject.c_str()); lua_pushstring(L, cInfo.issuer.c_str()); - lua_pushnumber(L, cInfo.version); + lua_pushinteger(L, cInfo.version); lua_pushstring(L, cInfo.serial_number.c_str()); lua_pushstring(L, cInfo.time_not_before.c_str()); lua_pushstring(L, cInfo.time_not_after.c_str()); @@ -55,7 +64,7 @@ static int l_getX509Infos(lua_State *L) lua_pushstring(L, cInfo.v3ext_subjkey_id.c_str()); lua_pushstring(L, cInfo.v3ext_subjkey_id_critical.c_str()); - return 18; // Note: the lua stack has only (at least) 20 free slots! + return 18; // Note: the lua stack has only (at least) 20 free slots! } Decoder v2g_message_decoder; @@ -183,6 +192,7 @@ extern "C" {"initValidator", l_validate_init}, {"cleanupValidator", l_validate_cleanup}, {"getX509Infos", l_getX509Infos}, + {"getGnuTLSErrorDescr", l_getGnuTlsErrorDescription}, {NULL, NULL}}; #if LUA_VERSION_NUM > 501 luaL_newlib(L, LuaDecoderLib); @@ -191,4 +201,4 @@ extern "C" #endif return 1; } -} \ No newline at end of file +} diff --git a/Wireshark/plugins/v2gmsg.lua b/Wireshark/plugins/v2gmsg.lua index 83c2f9d..6c143a8 100644 --- a/Wireshark/plugins/v2gmsg.lua +++ b/Wireshark/plugins/v2gmsg.lua @@ -137,63 +137,76 @@ local function add_expert_info(message, tree, pinfo, expertinfo) end end +local function add_certificate_subtree(xml_table, cert_element, dissector_field, pinfo) + + if string.len(xml_table.value) > 150 then -- cut too long strings + cert_element:set_text(xml_table.name .. ": " .. xml_table.value:sub(0, 150) .. "(...)") + else + cert_element:set_text(xml_table.name .. ": " .. xml_table.value) + end + + local result_code, + subj, + issuer, + version, + serial, + not_before, + not_after, + sig_algo, + sig_value, + pk_algo, + spk_curve, + spk_pub, + v3_constraint, + v3_constraint_CA, + v3_key_usage, + v3_key_usage_crit, + v3_sk_ID, + v3_sk_ID_crit = v2g_lib.getX509Infos(xml_table.value) + if result_code < 0 then + local cert_err_tree = cert_element:add(dissector_field, math.floor(result_code)) + local err_description = v2g_lib.getGnuTLSErrorDescr(math.floor(result_code)) + cert_err_tree:set_text("GnuTLS error code: " .. math.floor(result_code) .. " (" .. err_description .. ")") + add_expert_info("A GnuTLS error (" .. math.floor(result_code) .. ") was encountered while trying to process this certificate.", cert_err_tree, pinfo, ef_warning_generic) + end + cert_element:add(dissector_field, subj):set_text("Subject: " .. subj) + cert_element:add(dissector_field, issuer):set_text("Issuer: " .. issuer) + if version < 0 then + cert_element:add(dissector_field, version):set_text( + "Version: ERROR" + ) + else + cert_element:add(dissector_field, version):set_text( + "Version: v" .. (version + 1) .. " (" .. version .. ")" + ) -- certificate version is always +1 according to the standard + end + cert_element:add(dissector_field, serial):set_text("Serial Number: " .. serial) + cert_element:add(dissector_field, not_before):set_text("Not Valid Before: " .. not_before) + cert_element:add(dissector_field, not_after):set_text("Not Valid After: " .. not_after) + cert_element:add(dissector_field, sig_algo):set_text("Signature Algorithm: " .. sig_algo) + cert_element:add(dissector_field, sig_value):set_text("Signature Value: " .. sig_value) + cert_element:add(dissector_field, pk_algo):set_text("Public Key Algorithm: " .. pk_algo) + cert_element:add(dissector_field, spk_curve):set_text("Subject Public Key - Curve: " .. spk_curve) + cert_element:add(dissector_field, spk_pub):set_text("Subject Public Key - RAW: " .. spk_pub) + local x509_v3_element = cert_element:add(dissector_field, "X509v3") + x509_v3_element:set_text("X509v3") + x509_v3_element:add(dissector_field, v3_constraint):set_text("Basic Constraint: " .. v3_constraint) + x509_v3_element:add(dissector_field, v3_constraint_CA):set_text( + "Basic Constraint CA: " .. v3_constraint_CA + ) + x509_v3_element:add(dissector_field, v3_key_usage_crit):set_text("Key Usage: " .. v3_key_usage_crit) + x509_v3_element:add(dissector_field, v3_key_usage):set_text("Key Usage: " .. v3_key_usage) + x509_v3_element:add(dissector_field, v3_sk_ID_crit):set_text("Subject Key ID: " .. v3_sk_ID_crit) + x509_v3_element:add(dissector_field, v3_sk_ID):set_text("Subject Key ID: " .. v3_sk_ID) +end + local function add_xml_table_to_tree(xml_table, tree_out, dissector_field, pinfo) local new_element if xml_table.value ~= "" then -- special handling for certificates if xml_table.name == "Certificate" or xml_table.name == "OEMProvisioningCert" then local cert_element = tree_out:add(dissector_field, xml_table.value) - if string.len(xml_table.value) > 150 then -- cut too long strings - cert_element:set_text(xml_table.name .. ": " .. xml_table.value:sub(0, 150) .. "(...)") - else - cert_element:set_text(xml_table.name .. ": " .. xml_table.value) - end - - local valid, - subj, - issuer, - version, - serial, - not_before, - not_after, - sig_algo, - sig_value, - pk_algo, - spk_curve, - spk_pub, - v3_constraint, - v3_constraint_CA, - v3_key_usage, - v3_key_usage_crit, - v3_sk_ID, - v3_sk_ID_crit = v2g_lib.getX509Infos(xml_table.value) - if valid then - cert_element:add(dissector_field, subj):set_text("Subject: " .. subj) - cert_element:add(dissector_field, issuer):set_text("Issuer: " .. issuer) - cert_element:add(dissector_field, version):set_text( - "Version: v" .. (version + 1) .. " (" .. version .. ")" - ) -- certificate version is always +1 according to the standard - cert_element:add(dissector_field, serial):set_text("Serial Number: 0x" .. serial) - cert_element:add(dissector_field, not_before):set_text("Not Valid Before: " .. not_before) - cert_element:add(dissector_field, not_after):set_text("Not Valid After: " .. not_after) - cert_element:add(dissector_field, sig_algo):set_text("Signature Algorithm: " .. sig_algo) - cert_element:add(dissector_field, sig_value):set_text("Signature Value: " .. sig_value) - cert_element:add(dissector_field, pk_algo):set_text("Public Key Algorithm: " .. pk_algo) - cert_element:add(dissector_field, spk_curve):set_text("Subject Public Key - Curve: " .. spk_curve) - cert_element:add(dissector_field, spk_pub):set_text("Subject Public Key - RAW: " .. spk_pub) - local x509_v3_element = cert_element:add(dissector_field, "X509v3") - x509_v3_element:set_text("X509v3") - x509_v3_element:add(dissector_field, v3_constraint):set_text("Basic Constraint: " .. v3_constraint) - x509_v3_element:add(dissector_field, v3_constraint_CA):set_text( - "Basic Constraint CA: " .. v3_constraint_CA - ) - x509_v3_element:add(dissector_field, v3_key_usage_crit):set_text("Key Usage: " .. v3_key_usage_crit) - x509_v3_element:add(dissector_field, v3_key_usage):set_text("Key Usage: " .. v3_key_usage) - x509_v3_element:add(dissector_field, v3_sk_ID_crit):set_text("Subject Key ID: " .. v3_sk_ID_crit) - x509_v3_element:add(dissector_field, v3_sk_ID):set_text("Subject Key ID: " .. v3_sk_ID) - else - add_expert_info("INVALID CERTIFICATE", cert_element, pinfo, ef_warning_generic) - end + add_certificate_subtree(xml_table, cert_element, dissector_field, pinfo) else new_element = tree_out:add(dissector_field, xml_table.value) new_element:set_text(xml_table.name .. ": " .. xml_table.value) From bf28ffc3427305b3fb193c34105f3891293e6cdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoren=20Gr=C3=BCttemeier?= Date: Fri, 20 Sep 2024 13:17:30 +0200 Subject: [PATCH 3/7] updated GnuTLS headers to 3.8.4 --- .../GnuTLS/include/gnutls/gnutls.h | 10 +- .../GnuTLS/include/gnutls/gnutlsxx.h | 1267 +++++++++++++++++ .../GnuTLS/include/gnutls/openssl.h | 312 ---- .../Third_Party/GnuTLS/include/gnutls/x509.h | 8 + 4 files changed, 1281 insertions(+), 316 deletions(-) create mode 100644 V2G_Libraries/Third_Party/GnuTLS/include/gnutls/gnutlsxx.h delete mode 100644 V2G_Libraries/Third_Party/GnuTLS/include/gnutls/openssl.h diff --git a/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/gnutls.h b/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/gnutls.h index fdd76ef..50ab8b6 100644 --- a/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/gnutls.h +++ b/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/gnutls.h @@ -49,14 +49,14 @@ extern "C" { #endif -#define GNUTLS_VERSION "3.8.3" +#define GNUTLS_VERSION "3.8.4" /* clang-format off */ #define GNUTLS_VERSION_MAJOR 3 #define GNUTLS_VERSION_MINOR 8 -#define GNUTLS_VERSION_PATCH 3 +#define GNUTLS_VERSION_PATCH 4 -#define GNUTLS_VERSION_NUMBER 0x030803 +#define GNUTLS_VERSION_NUMBER 0x030804 /* clang-format on */ #define GNUTLS_CIPHER_RIJNDAEL_128_CBC GNUTLS_CIPHER_AES_128_CBC @@ -876,6 +876,7 @@ typedef enum gnutls_certificate_print_formats { * gnutls_pk_algorithm_t: * @GNUTLS_PK_UNKNOWN: Unknown public-key algorithm. * @GNUTLS_PK_RSA: RSA public-key algorithm. + * @GNUTLS_PK_RSA_OAEP: RSA public-key algorithm, with OAEP padding. * @GNUTLS_PK_RSA_PSS: RSA public-key algorithm, with PSS padding. * @GNUTLS_PK_DSA: DSA public-key algorithm. * @GNUTLS_PK_DH: Diffie-Hellman algorithm. Used to generate parameters. @@ -904,7 +905,8 @@ typedef enum { GNUTLS_PK_GOST_12_512 = 10, GNUTLS_PK_ECDH_X448 = 11, GNUTLS_PK_EDDSA_ED448 = 12, - GNUTLS_PK_MAX = GNUTLS_PK_EDDSA_ED448 + GNUTLS_PK_RSA_OAEP = 13, + GNUTLS_PK_MAX = GNUTLS_PK_RSA_OAEP } gnutls_pk_algorithm_t; const char *gnutls_pk_algorithm_get_name(gnutls_pk_algorithm_t algorithm); diff --git a/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/gnutlsxx.h b/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/gnutlsxx.h new file mode 100644 index 0000000..673cf53 --- /dev/null +++ b/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/gnutlsxx.h @@ -0,0 +1,1267 @@ +/* + * Copyright (C) 2006-2012 Free Software Foundation, Inc. + * + * Author: Nikos Mavrogiannopoulos + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see + * + */ + +#ifndef GNUTLS_GNUTLSXX_H +#define GNUTLS_GNUTLSXX_H + +#include +#include +#include + +namespace gnutls +{ + +class noncopyable { + protected: + noncopyable() + { + } + ~noncopyable() + { + } + + private: + /* These are non-implemented. + */ + noncopyable(const noncopyable &); + noncopyable &operator=(const noncopyable &); +}; + +class exception : public std::exception { + public: + explicit exception(int x); + const char *what() const throw(); + int get_code(); + + protected: + int retcode; +}; + +class dh_params : private noncopyable { + public: + dh_params(); + ~dh_params(); + void import_raw(const gnutls_datum_t &prime, + const gnutls_datum_t &generator); + void import_pkcs3(const gnutls_datum_t &pkcs3_params, + gnutls_x509_crt_fmt_t format); + void generate(unsigned int bits); + + void export_pkcs3(gnutls_x509_crt_fmt_t format, + unsigned char *params_data, size_t *params_data_size); + void export_raw(gnutls_datum_t &prime, gnutls_datum_t &generator); + + gnutls_dh_params_t get_params_t() const; + dh_params &operator=(const dh_params &src); + + protected: + gnutls_dh_params_t params; +}; + +class rsa_params : private noncopyable { + public: + rsa_params(); + ~rsa_params(); + void import_raw(const gnutls_datum_t &m, const gnutls_datum_t &e, + const gnutls_datum_t &d, const gnutls_datum_t &p, + const gnutls_datum_t &q, const gnutls_datum_t &u); + void import_pkcs1(const gnutls_datum_t &pkcs1_params, + gnutls_x509_crt_fmt_t format); + void generate(unsigned int bits); + + void export_pkcs1(gnutls_x509_crt_fmt_t format, + unsigned char *params_data, size_t *params_data_size); + void export_raw(gnutls_datum_t &m, gnutls_datum_t &e, gnutls_datum_t &d, + gnutls_datum_t &p, gnutls_datum_t &q, + gnutls_datum_t &u); + gnutls_rsa_params_t get_params_t() const; + rsa_params &operator=(const rsa_params &src); + + protected: + gnutls_rsa_params_t params; +}; + +class session : private noncopyable { + protected: + gnutls_session_t s; + + public: + explicit session(unsigned int); + virtual ~session(); + + gnutls_session_t ptr(); + int bye(gnutls_close_request_t how); + int handshake(); + + gnutls_alert_description_t get_alert() const; + + int send_alert(gnutls_alert_level_t level, + gnutls_alert_description_t desc); + int send_appropriate_alert(int err); + + gnutls_cipher_algorithm_t get_cipher() const; + gnutls_kx_algorithm_t get_kx() const; + gnutls_mac_algorithm_t get_mac() const; + gnutls_compression_method_t get_compression() const; + gnutls_certificate_type_t get_certificate_type() const; + + /* for the handshake + */ + void set_private_extensions(bool allow); + + gnutls_handshake_description_t get_handshake_last_out() const; + gnutls_handshake_description_t get_handshake_last_in() const; + + ssize_t send(const void *data, size_t sizeofdata); + ssize_t recv(void *data, size_t sizeofdata); + + bool get_record_direction() const; + + /* maximum packet size + */ + size_t get_max_size() const; + void set_max_size(size_t size); + + size_t check_pending() const; + + void prf(size_t label_size, const char *label, int server_random_first, + size_t extra_size, const char *extra, size_t outsize, + char *out); + + void prf_raw(size_t label_size, const char *label, size_t seed_size, + const char *seed, size_t outsize, char *out); + + /* if you just want some defaults, use the following. + */ + void set_priority(const char *prio, const char **err_pos); + void set_priority(gnutls_priority_t p); + + gnutls_protocol_t get_protocol_version() const; + + /* for resuming sessions + */ + void set_data(const void *session_data, size_t session_data_size); + void get_data(void *session_data, size_t *session_data_size) const; + void get_data(gnutls_session_t session, gnutls_datum_t &data) const; + void get_id(void *session_id, size_t *session_id_size) const; + + bool is_resumed() const; + + void set_max_handshake_packet_length(size_t max); + + void clear_credentials(); + void set_credentials(const class credentials &cred); + + void set_transport_ptr(gnutls_transport_ptr_t ptr); + void set_transport_ptr(gnutls_transport_ptr_t recv_ptr, + gnutls_transport_ptr_t send_ptr); + gnutls_transport_ptr_t get_transport_ptr() const; + void get_transport_ptr(gnutls_transport_ptr_t &recv_ptr, + gnutls_transport_ptr_t &send_ptr) const; + + void set_transport_lowat(size_t num); + void set_transport_push_function(gnutls_push_func push_func); + void + set_transport_vec_push_function(gnutls_vec_push_func vec_push_func); + void set_transport_pull_function(gnutls_pull_func pull_func); + void set_transport_pull_timeout_function( + gnutls_pull_timeout_func pull_timeout_func); + + void set_user_ptr(void *ptr); + void *get_user_ptr() const; + + void send_openpgp_cert(gnutls_openpgp_crt_status_t status); + + gnutls_credentials_type_t get_auth_type() const; + gnutls_credentials_type_t get_server_auth_type() const; + gnutls_credentials_type_t get_client_auth_type() const; + + /* informational stuff + */ + void set_dh_prime_bits(unsigned int bits); + unsigned int get_dh_secret_bits() const; + unsigned int get_dh_peers_public_bits() const; + unsigned int get_dh_prime_bits() const; + void get_dh_group(gnutls_datum_t &gen, gnutls_datum_t &prime) const; + void get_dh_pubkey(gnutls_datum_t &raw_key) const; + void get_rsa_export_pubkey(gnutls_datum_t &exponent, + gnutls_datum_t &modulus) const; + unsigned int get_rsa_export_modulus_bits() const; + + void get_our_certificate(gnutls_datum_t &cert) const; + bool + get_peers_certificate(std::vector &out_certs) const; + bool get_peers_certificate(const gnutls_datum_t **certs, + unsigned int *certs_size) const; + + time_t get_peers_certificate_activation_time() const; + time_t get_peers_certificate_expiration_time() const; + void verify_peers_certificate(unsigned int &status) const; +}; + +/* interface for databases + */ +class DB : private noncopyable { + public: + virtual ~DB() = 0; + virtual bool store(const gnutls_datum_t &key, + const gnutls_datum_t &data) = 0; + virtual bool retrieve(const gnutls_datum_t &key, + gnutls_datum_t &data) = 0; + virtual bool remove(const gnutls_datum_t &key) = 0; +}; + +class server_session : public session { + public: + server_session(); + explicit server_session(int flags); + ~server_session(); + void db_remove() const; + + void set_db_cache_expiration(unsigned int seconds); + void set_db(const DB &db); + + /* returns true if session is expired + */ + bool db_check_entry(const gnutls_datum_t &session_data) const; + + /* server side only + */ + const char *get_srp_username() const; + const char *get_psk_username() const; + + void get_server_name(void *data, size_t *data_length, + unsigned int *type, unsigned int indx) const; + + int rehandshake(); + void set_certificate_request(gnutls_certificate_request_t); +}; + +class client_session : public session { + public: + client_session(); + explicit client_session(int flags); + ~client_session(); + + void set_verify_cert(const char *hostname, unsigned flags); + void set_server_name(gnutls_server_name_type_t type, const void *name, + size_t name_length); + + bool get_request_status(); +}; + +class credentials : private noncopyable { + public: + virtual ~credentials() + { + } + gnutls_credentials_type_t get_type() const; + + protected: + friend class session; + explicit credentials(gnutls_credentials_type_t t); + void *ptr() const; + void set_ptr(void *ptr); + gnutls_credentials_type_t type; + + private: + void *cred; +}; + +class certificate_credentials : public credentials { + public: + ~certificate_credentials(); + certificate_credentials(); + + void free_keys(); + void free_cas(); + void free_ca_names(); + void free_crls(); + + void set_dh_params(const dh_params ¶ms); + void set_rsa_export_params(const rsa_params ¶ms); + void set_verify_flags(unsigned int flags); + void set_verify_limits(unsigned int max_bits, unsigned int max_depth); + + void set_x509_trust_file(const char *cafile, + gnutls_x509_crt_fmt_t type); + void set_x509_trust(const gnutls_datum_t &CA, + gnutls_x509_crt_fmt_t type); + + void set_x509_trust(gnutls_x509_crt_t *ca_list, int ca_list_size); + + void set_x509_crl_file(const char *crlfile, gnutls_x509_crt_fmt_t type); + void set_x509_crl(const gnutls_datum_t &CRL, + gnutls_x509_crt_fmt_t type); + void set_x509_crl(gnutls_x509_crl_t *crl_list, int crl_list_size); + + void set_x509_key_file(const char *certfile, const char *KEYFILE, + gnutls_x509_crt_fmt_t type); + void set_x509_key(const gnutls_datum_t &CERT, const gnutls_datum_t &KEY, + gnutls_x509_crt_fmt_t type); + + void set_x509_key(gnutls_x509_crt_t *cert_list, int cert_list_size, + gnutls_x509_privkey_t key); + + void set_simple_pkcs12_file(const char *pkcs12file, + gnutls_x509_crt_fmt_t type, + const char *password); + + void set_retrieve_function(gnutls_certificate_retrieve_function *func); + + protected: + gnutls_certificate_credentials_t cred; +}; + +class certificate_server_credentials : public certificate_credentials { + public: + void set_params_function(gnutls_params_function *func); +}; + +class certificate_client_credentials : public certificate_credentials { + public: +}; + +class anon_server_credentials : public credentials { + public: + anon_server_credentials(); + ~anon_server_credentials(); + void set_dh_params(const dh_params ¶ms); + void set_params_function(gnutls_params_function *func); + + protected: + gnutls_anon_server_credentials_t cred; +}; + +class anon_client_credentials : public credentials { + public: + anon_client_credentials(); + ~anon_client_credentials(); + + protected: + gnutls_anon_client_credentials_t cred; +}; + +class srp_server_credentials : public credentials { + public: + srp_server_credentials(); + ~srp_server_credentials(); + void set_credentials_file(const char *password_file, + const char *password_conf_file); + void + set_credentials_function(gnutls_srp_server_credentials_function *func); + + protected: + gnutls_srp_server_credentials_t cred; +}; + +class srp_client_credentials : public credentials { + public: + srp_client_credentials(); + ~srp_client_credentials(); + void set_credentials(const char *username, const char *password); + void + set_credentials_function(gnutls_srp_client_credentials_function *func); + + protected: + gnutls_srp_client_credentials_t cred; +}; + +class psk_server_credentials : public credentials { + public: + psk_server_credentials(); + ~psk_server_credentials(); + void set_credentials_file(const char *password_file); + void + set_credentials_function(gnutls_psk_server_credentials_function *func); + void set_dh_params(const dh_params ¶ms); + void set_params_function(gnutls_params_function *func); + + protected: + gnutls_psk_server_credentials_t cred; +}; + +class psk_client_credentials : public credentials { + public: + psk_client_credentials(); + ~psk_client_credentials(); + void set_credentials(const char *username, const gnutls_datum_t &key, + gnutls_psk_key_flags flags); + void + set_credentials_function(gnutls_psk_client_credentials_function *func); + + protected: + gnutls_psk_client_credentials_t cred; +}; + +/* By default, we provide the function definitions, which allows users + of the library to use the C++ header and link against the C + library. However, if GNUTLS_GNUTLSXX_NO_HEADERONLY is defined, then + the definitions are not necessary, as the user is expected to link + to the C++ library. (Which is provided for backwards-compatibility.) + + All applications using GnuTLS of version less than 3.8.0 use the + C++ library. Applications using GnuTLS 3.8.0 or above will use by + default the C library with the C++ "header-only" header, but they + still have the option to link to the C++ library instead if they + wish, and if so, they must also define + GNUTLS_GNUTLSXX_NO_HEADERONLY in their compilation step. + */ +#ifndef GNUTLS_GNUTLSXX_NO_HEADERONLY + +inline static int RETWRAP(int ret) +{ + if (ret < 0) + throw(exception(ret)); + return ret; +} + +session::session(unsigned int flags) +{ + RETWRAP(gnutls_init(&s, flags)); +} + +session::~session() +{ + gnutls_deinit(s); +} + +gnutls_session_t session::ptr() +{ + return s; +} + +int session::bye(gnutls_close_request_t how) +{ + return RETWRAP(gnutls_bye(s, how)); +} + +int session::handshake() +{ + return RETWRAP(gnutls_handshake(s)); +} + +server_session::server_session() + : session(GNUTLS_SERVER) +{ +} + +server_session::server_session(int flags) + : session(GNUTLS_SERVER | (flags & ~GNUTLS_CLIENT)) +{ +} + +server_session::~server_session() +{ +} + +int server_session::rehandshake() +{ + return RETWRAP(gnutls_rehandshake(s)); +} + +gnutls_alert_description_t session::get_alert() const +{ + return gnutls_alert_get(s); +} +int session::send_alert(gnutls_alert_level_t level, + gnutls_alert_description_t desc) +{ + return RETWRAP(gnutls_alert_send(s, level, desc)); +} + +int session::send_appropriate_alert(int err) +{ + return RETWRAP(gnutls_alert_send_appropriate(s, err)); +} + +gnutls_cipher_algorithm_t session::get_cipher() const +{ + return gnutls_cipher_get(s); +} +gnutls_kx_algorithm_t session::get_kx() const +{ + return gnutls_kx_get(s); +} +gnutls_mac_algorithm_t session::get_mac() const +{ + return gnutls_mac_get(s); +} +gnutls_compression_method_t session::get_compression() const +{ + return gnutls_compression_get(s); +} +gnutls_certificate_type_t session::get_certificate_type() const +{ + return gnutls_certificate_type_get(s); +} +void session::set_private_extensions(bool allow) +{ + gnutls_handshake_set_private_extensions(s, (int)allow); +} + +gnutls_handshake_description_t session::get_handshake_last_out() const +{ + return gnutls_handshake_get_last_out(s); +} +gnutls_handshake_description_t session::get_handshake_last_in() const +{ + return gnutls_handshake_get_last_in(s); +} +ssize_t session::send(const void *data, size_t sizeofdata) +{ + return RETWRAP(gnutls_record_send(s, data, sizeofdata)); +} + +ssize_t session::recv(void *data, size_t sizeofdata) +{ + return RETWRAP(gnutls_record_recv(s, data, sizeofdata)); +} + +bool session::get_record_direction() const +{ + return gnutls_record_get_direction(s); +} +/* maximum packet size + */ +size_t session::get_max_size() const +{ + return gnutls_record_get_max_size(s); +} +void session::set_max_size(size_t size) +{ + RETWRAP(gnutls_record_set_max_size(s, size)); +} + +size_t session::check_pending() const +{ + return gnutls_record_check_pending(s); +} +void session::prf(size_t label_size, const char *label, int server_random_first, + size_t extra_size, const char *extra, size_t outsize, + char *out) +{ + RETWRAP(gnutls_prf(s, label_size, label, server_random_first, + extra_size, extra, outsize, out)); +} + +void session::prf_raw(size_t label_size, const char *label, size_t seed_size, + const char *seed, size_t outsize, char *out) +{ + RETWRAP(gnutls_prf_raw(s, label_size, label, seed_size, seed, outsize, + out)); +} + +/* if you just want some defaults, use the following. + */ +void session::set_priority(const char *prio, const char **err_pos) +{ + RETWRAP(gnutls_priority_set_direct(s, prio, err_pos)); +} + +void session::set_priority(gnutls_priority_t p) +{ + RETWRAP(gnutls_priority_set(s, p)); +} + +gnutls_protocol_t session::get_protocol_version() const +{ + return gnutls_protocol_get_version(s); +} +void session::set_data(const void *session_data, size_t session_data_size) +{ + RETWRAP(gnutls_session_set_data(s, session_data, session_data_size)); +} + +void session::get_data(void *session_data, size_t *session_data_size) const +{ + RETWRAP(gnutls_session_get_data(s, session_data, session_data_size)); +} +void session::get_data(gnutls_session_t session, gnutls_datum_t &data) const +{ + RETWRAP(gnutls_session_get_data2(s, &data)); +} +void session::get_id(void *session_id, size_t *session_id_size) const +{ + RETWRAP(gnutls_session_get_id(s, session_id, session_id_size)); +} +bool session::is_resumed() const +{ + int ret = gnutls_session_is_resumed(s); + + return (ret != 0); +} +bool session::get_peers_certificate(std::vector &out_certs) const +{ + const gnutls_datum_t *certs; + unsigned int certs_size; + + certs = gnutls_certificate_get_peers(s, &certs_size); + + if (certs == NULL) + return false; + + for (unsigned int i = 0; i < certs_size; i++) + out_certs.push_back(certs[i]); + + return true; +} +bool session::get_peers_certificate(const gnutls_datum_t **certs, + unsigned int *certs_size) const +{ + *certs = gnutls_certificate_get_peers(s, certs_size); + + if (*certs == NULL) + return false; + return true; +} +void session::get_our_certificate(gnutls_datum_t &cert) const +{ + const gnutls_datum_t *d; + + d = gnutls_certificate_get_ours(s); + if (d == NULL) + throw(exception(GNUTLS_E_INVALID_REQUEST)); + + cert = *d; +} +time_t session::get_peers_certificate_activation_time() const +{ + return gnutls_certificate_activation_time_peers(s); +} +time_t session::get_peers_certificate_expiration_time() const +{ + return gnutls_certificate_expiration_time_peers(s); +} +void session::verify_peers_certificate(unsigned int &status) const +{ + RETWRAP(gnutls_certificate_verify_peers2(s, &status)); +} +client_session::client_session() + : session(GNUTLS_CLIENT) +{ +} + +client_session::client_session(int flags) + : session(GNUTLS_CLIENT | (flags & ~GNUTLS_SERVER)) +{ +} + +client_session::~client_session() +{ +} + +// client session +void client_session::set_verify_cert(const char *hostname, unsigned flags) +{ + gnutls_session_set_verify_cert(s, hostname, flags); +} + +void client_session::set_server_name(gnutls_server_name_type_t type, + const void *name, size_t name_length) +{ + RETWRAP(gnutls_server_name_set(s, type, name, name_length)); +} + +bool client_session::get_request_status() +{ + return RETWRAP(gnutls_certificate_client_get_request_status(s)); +} + +// server_session +void server_session::get_server_name(void *data, size_t *data_length, + unsigned int *type, + unsigned int indx) const +{ + RETWRAP(gnutls_server_name_get(s, data, data_length, type, indx)); +} +// internal DB stuff +static int store_function(void *_db, gnutls_datum_t key, gnutls_datum_t data) +{ + try { + DB *db = static_cast(_db); + + if (db->store(key, data) == false) + return -1; + } catch (...) { + return -1; + } + + return 0; +} + +const static gnutls_datum_t null_datum = { NULL, 0 }; + +static gnutls_datum_t retrieve_function(void *_db, gnutls_datum_t key) +{ + gnutls_datum_t data; + + try { + DB *db = static_cast(_db); + + if (db->retrieve(key, data) == false) + return null_datum; + + } catch (...) { + return null_datum; + } + + return data; +} + +static int remove_function(void *_db, gnutls_datum_t key) +{ + try { + DB *db = static_cast(_db); + + if (db->remove(key) == false) + return -1; + } catch (...) { + return -1; + } + + return 0; +} + +void server_session::set_db(const DB &db) +{ + gnutls_db_set_ptr(s, const_cast(&db)); + gnutls_db_set_store_function(s, store_function); + gnutls_db_set_retrieve_function(s, retrieve_function); + gnutls_db_set_remove_function(s, remove_function); +} + +void server_session::set_db_cache_expiration(unsigned int seconds) +{ + gnutls_db_set_cache_expiration(s, seconds); +} + +void server_session::db_remove() const +{ + gnutls_db_remove_session(s); +} +bool server_session::db_check_entry(const gnutls_datum_t &session_data) const +{ + int ret = gnutls_db_check_entry(s, session_data); + + if (ret != 0) + return true; + return false; +} +void session::set_max_handshake_packet_length(size_t max) +{ + gnutls_handshake_set_max_packet_length(s, max); +} + +void session::clear_credentials() +{ + gnutls_credentials_clear(s); +} + +void session::set_credentials(const credentials &cred) +{ + RETWRAP(gnutls_credentials_set(s, cred.get_type(), cred.ptr())); +} + +const char *server_session::get_srp_username() const +{ + return gnutls_srp_server_get_username(s); +} +const char *server_session::get_psk_username() const +{ + return gnutls_psk_server_get_username(s); +} +void session::set_transport_ptr(gnutls_transport_ptr_t ptr) +{ + gnutls_transport_set_ptr(s, ptr); +} + +void session::set_transport_ptr(gnutls_transport_ptr_t recv_ptr, + gnutls_transport_ptr_t send_ptr) +{ + gnutls_transport_set_ptr2(s, recv_ptr, send_ptr); +} + +gnutls_transport_ptr_t session::get_transport_ptr() const +{ + return gnutls_transport_get_ptr(s); +} +void session::get_transport_ptr(gnutls_transport_ptr_t &recv_ptr, + gnutls_transport_ptr_t &send_ptr) const +{ + gnutls_transport_get_ptr2(s, &recv_ptr, &send_ptr); +} +void session::set_transport_lowat(size_t num) +{ + throw(exception(GNUTLS_E_UNIMPLEMENTED_FEATURE)); +} + +void session::set_transport_push_function(gnutls_push_func push_func) +{ + gnutls_transport_set_push_function(s, push_func); +} + +void session::set_transport_vec_push_function(gnutls_vec_push_func vec_push_func) +{ + gnutls_transport_set_vec_push_function(s, vec_push_func); +} + +void session::set_transport_pull_function(gnutls_pull_func pull_func) +{ + gnutls_transport_set_pull_function(s, pull_func); +} + +void session::set_transport_pull_timeout_function( + gnutls_pull_timeout_func pull_timeout_func) +{ + gnutls_transport_set_pull_timeout_function(s, pull_timeout_func); +} + +void session::set_user_ptr(void *ptr) +{ + gnutls_session_set_ptr(s, ptr); +} + +void *session::get_user_ptr() const +{ + return gnutls_session_get_ptr(s); +} +void session::send_openpgp_cert(gnutls_openpgp_crt_status_t status) +{ + gnutls_openpgp_send_cert(s, status); +} + +void session::set_dh_prime_bits(unsigned int bits) +{ + gnutls_dh_set_prime_bits(s, bits); +} + +unsigned int session::get_dh_secret_bits() const +{ + return RETWRAP(gnutls_dh_get_secret_bits(s)); +} +unsigned int session::get_dh_peers_public_bits() const +{ + return RETWRAP(gnutls_dh_get_peers_public_bits(s)); +} +unsigned int session::get_dh_prime_bits() const +{ + return RETWRAP(gnutls_dh_get_prime_bits(s)); +} +void session::get_dh_group(gnutls_datum_t &gen, gnutls_datum_t &prime) const +{ + RETWRAP(gnutls_dh_get_group(s, &gen, &prime)); +} +void session::get_dh_pubkey(gnutls_datum_t &raw_key) const +{ + RETWRAP(gnutls_dh_get_pubkey(s, &raw_key)); +} +void server_session::set_certificate_request(gnutls_certificate_request_t req) +{ + gnutls_certificate_server_set_request(s, req); +} + +gnutls_credentials_type_t session::get_auth_type() const +{ + return gnutls_auth_get_type(s); +} +gnutls_credentials_type_t session::get_server_auth_type() const +{ + return gnutls_auth_server_get_type(s); +} +gnutls_credentials_type_t session::get_client_auth_type() const +{ + return gnutls_auth_client_get_type(s); +} +certificate_credentials::~certificate_credentials() +{ + gnutls_certificate_free_credentials(cred); +} + +certificate_credentials::certificate_credentials() + : credentials(GNUTLS_CRD_CERTIFICATE) +{ + RETWRAP(gnutls_certificate_allocate_credentials(&cred)); + set_ptr(cred); +} + +void certificate_server_credentials::set_params_function( + gnutls_params_function *func) +{ + gnutls_certificate_set_params_function(cred, func); +} + +anon_server_credentials::anon_server_credentials() + : credentials(GNUTLS_CRD_ANON) +{ + RETWRAP(gnutls_anon_allocate_server_credentials(&cred)); + set_ptr(cred); +} + +anon_server_credentials::~anon_server_credentials() +{ + gnutls_anon_free_server_credentials(cred); +} + +void anon_server_credentials::set_dh_params(const dh_params ¶ms) +{ + gnutls_anon_set_server_dh_params(cred, params.get_params_t()); +} + +void anon_server_credentials::set_params_function(gnutls_params_function *func) +{ + gnutls_anon_set_server_params_function(cred, func); +} + +anon_client_credentials::anon_client_credentials() + : credentials(GNUTLS_CRD_ANON) +{ + RETWRAP(gnutls_anon_allocate_client_credentials(&cred)); + set_ptr(cred); +} + +anon_client_credentials::~anon_client_credentials() +{ + gnutls_anon_free_client_credentials(cred); +} + +void certificate_credentials::free_keys() +{ + gnutls_certificate_free_keys(cred); +} + +void certificate_credentials::free_cas() +{ + gnutls_certificate_free_cas(cred); +} + +void certificate_credentials::free_ca_names() +{ + gnutls_certificate_free_ca_names(cred); +} + +void certificate_credentials::free_crls() +{ + gnutls_certificate_free_crls(cred); +} + +void certificate_credentials::set_dh_params(const dh_params ¶ms) +{ + gnutls_certificate_set_dh_params(cred, params.get_params_t()); +} + +void certificate_credentials::set_verify_flags(unsigned int flags) +{ + gnutls_certificate_set_verify_flags(cred, flags); +} + +void certificate_credentials::set_verify_limits(unsigned int max_bits, + unsigned int max_depth) +{ + gnutls_certificate_set_verify_limits(cred, max_bits, max_depth); +} + +void certificate_credentials::set_x509_trust_file(const char *cafile, + gnutls_x509_crt_fmt_t type) +{ + RETWRAP(gnutls_certificate_set_x509_trust_file(cred, cafile, type)); +} + +void certificate_credentials::set_x509_trust(const gnutls_datum_t &CA, + gnutls_x509_crt_fmt_t type) +{ + RETWRAP(gnutls_certificate_set_x509_trust_mem(cred, &CA, type)); +} + +void certificate_credentials::set_x509_crl_file(const char *crlfile, + gnutls_x509_crt_fmt_t type) +{ + RETWRAP(gnutls_certificate_set_x509_crl_file(cred, crlfile, type)); +} + +void certificate_credentials::set_x509_crl(const gnutls_datum_t &CRL, + gnutls_x509_crt_fmt_t type) +{ + RETWRAP(gnutls_certificate_set_x509_crl_mem(cred, &CRL, type)); +} + +void certificate_credentials::set_x509_key_file(const char *certfile, + const char *keyfile, + gnutls_x509_crt_fmt_t type) +{ + RETWRAP(gnutls_certificate_set_x509_key_file(cred, certfile, keyfile, + type)); +} + +void certificate_credentials::set_x509_key(const gnutls_datum_t &CERT, + const gnutls_datum_t &KEY, + gnutls_x509_crt_fmt_t type) +{ + RETWRAP(gnutls_certificate_set_x509_key_mem(cred, &CERT, &KEY, type)); +} + +void certificate_credentials::set_simple_pkcs12_file(const char *pkcs12file, + gnutls_x509_crt_fmt_t type, + const char *password) +{ + RETWRAP(gnutls_certificate_set_x509_simple_pkcs12_file(cred, pkcs12file, + type, password)); +} + +void certificate_credentials::set_x509_key(gnutls_x509_crt_t *cert_list, + int cert_list_size, + gnutls_x509_privkey_t key) +{ + RETWRAP(gnutls_certificate_set_x509_key(cred, cert_list, cert_list_size, + key)); +} + +void certificate_credentials::set_x509_trust(gnutls_x509_crt_t *ca_list, + int ca_list_size) +{ + RETWRAP(gnutls_certificate_set_x509_trust(cred, ca_list, ca_list_size)); +} + +void certificate_credentials::set_x509_crl(gnutls_x509_crl_t *crl_list, + int crl_list_size) +{ + RETWRAP(gnutls_certificate_set_x509_crl(cred, crl_list, crl_list_size)); +} + +void certificate_credentials::set_retrieve_function( + gnutls_certificate_retrieve_function *func) +{ + gnutls_certificate_set_retrieve_function(cred, func); +} + +// SRP + +srp_server_credentials::srp_server_credentials() + : credentials(GNUTLS_CRD_SRP) +{ + RETWRAP(gnutls_srp_allocate_server_credentials(&cred)); + set_ptr(cred); +} + +srp_server_credentials::~srp_server_credentials() +{ + gnutls_srp_free_server_credentials(cred); +} + +srp_client_credentials::srp_client_credentials() + : credentials(GNUTLS_CRD_SRP) +{ + RETWRAP(gnutls_srp_allocate_client_credentials(&cred)); + set_ptr(cred); +} + +srp_client_credentials::~srp_client_credentials() +{ + gnutls_srp_free_client_credentials(cred); +} + +void srp_client_credentials::set_credentials(const char *username, + const char *password) +{ + RETWRAP(gnutls_srp_set_client_credentials(cred, username, password)); +} + +void srp_server_credentials::set_credentials_file( + const char *password_file, const char *password_conf_file) +{ + RETWRAP(gnutls_srp_set_server_credentials_file(cred, password_file, + password_conf_file)); +} + +void srp_server_credentials::set_credentials_function( + gnutls_srp_server_credentials_function *func) +{ + gnutls_srp_set_server_credentials_function(cred, func); +} + +void srp_client_credentials::set_credentials_function( + gnutls_srp_client_credentials_function *func) +{ + gnutls_srp_set_client_credentials_function(cred, func); +} + +// PSK + +psk_server_credentials::psk_server_credentials() + : credentials(GNUTLS_CRD_PSK) +{ + RETWRAP(gnutls_psk_allocate_server_credentials(&cred)); + set_ptr(cred); +} + +psk_server_credentials::~psk_server_credentials() +{ + gnutls_psk_free_server_credentials(cred); +} + +void psk_server_credentials::set_credentials_file(const char *password_file) +{ + RETWRAP(gnutls_psk_set_server_credentials_file(cred, password_file)); +} + +void psk_server_credentials::set_credentials_function( + gnutls_psk_server_credentials_function *func) +{ + gnutls_psk_set_server_credentials_function(cred, func); +} + +void psk_server_credentials::set_dh_params(const dh_params ¶ms) +{ + gnutls_psk_set_server_dh_params(cred, params.get_params_t()); +} + +void psk_server_credentials::set_params_function(gnutls_params_function *func) +{ + gnutls_psk_set_server_params_function(cred, func); +} + +psk_client_credentials::psk_client_credentials() + : credentials(GNUTLS_CRD_PSK) +{ + RETWRAP(gnutls_psk_allocate_client_credentials(&cred)); + set_ptr(cred); +} + +psk_client_credentials::~psk_client_credentials() +{ + gnutls_psk_free_client_credentials(cred); +} + +void psk_client_credentials::set_credentials(const char *username, + const gnutls_datum_t &key, + gnutls_psk_key_flags flags) +{ + RETWRAP(gnutls_psk_set_client_credentials(cred, username, &key, flags)); +} + +void psk_client_credentials::set_credentials_function( + gnutls_psk_client_credentials_function *func) +{ + gnutls_psk_set_client_credentials_function(cred, func); +} + +credentials::credentials(gnutls_credentials_type_t t) + : type(t) + , cred(NULL) +{ +} + +gnutls_credentials_type_t credentials::get_type() const +{ + return type; +} +void *credentials::ptr() const +{ + return cred; +} +void credentials::set_ptr(void *ptr) +{ + cred = ptr; +} + +exception::exception(int x) +{ + retcode = x; +} + +int exception::get_code() +{ + return retcode; +} + +const char *exception::what() const throw() +{ + return gnutls_strerror(retcode); +} + +dh_params::dh_params() +{ + RETWRAP(gnutls_dh_params_init(¶ms)); +} + +dh_params::~dh_params() +{ + gnutls_dh_params_deinit(params); +} + +void dh_params::import_raw(const gnutls_datum_t &prime, + const gnutls_datum_t &generator) +{ + RETWRAP(gnutls_dh_params_import_raw(params, &prime, &generator)); +} + +void dh_params::import_pkcs3(const gnutls_datum_t &pkcs3_params, + gnutls_x509_crt_fmt_t format) +{ + RETWRAP(gnutls_dh_params_import_pkcs3(params, &pkcs3_params, format)); +} + +void dh_params::generate(unsigned int bits) +{ + RETWRAP(gnutls_dh_params_generate2(params, bits)); +} + +void dh_params::export_pkcs3(gnutls_x509_crt_fmt_t format, + unsigned char *params_data, + size_t *params_data_size) +{ + RETWRAP(gnutls_dh_params_export_pkcs3(params, format, params_data, + params_data_size)); +} + +void dh_params::export_raw(gnutls_datum_t &prime, gnutls_datum_t &generator) +{ + RETWRAP(gnutls_dh_params_export_raw(params, &prime, &generator, NULL)); +} + +gnutls_dh_params_t dh_params::get_params_t() const +{ + return params; +} +dh_params &dh_params::operator=(const dh_params &src) +{ + dh_params *dst = new dh_params; + int ret; + + ret = gnutls_dh_params_cpy(dst->params, src.params); + + if (ret < 0) { + delete dst; + throw(exception(ret)); + } + + std::swap(this->params, dst->params); + delete dst; + + return *this; +} + +#endif /* GNUTLS_GNUTLSXX_NO_HEADERONLY */ + +} /* namespace gnutls */ + +#endif /* GNUTLS_GNUTLSXX_H */ diff --git a/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/openssl.h b/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/openssl.h deleted file mode 100644 index 0383ee6..0000000 --- a/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/openssl.h +++ /dev/null @@ -1,312 +0,0 @@ -/* - * Copyright (C) 2004-2012 Free Software Foundation, Inc. - * Copyright (c) 2002 Andrew McDonald - * - * This file is part of GnuTLS-EXTRA. - * - * GnuTLS-extra 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-extra 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-EXTRA. If not, see . - */ - -/* WARNING: Error functions aren't currently thread-safe */ - -/* This file contains prototypes about the OpenSSL compatibility layer - * in GnuTLS. GnuTLS is not a complete replacement of OPENSSL so this - * compatibility layer only supports limited OpenSSL functionality. - * - * New programs should avoid using this compatibility layer, and use - * the native GnuTLS API directly. - */ - -#ifndef GNUTLS_OPENSSL_H -#define GNUTLS_OPENSSL_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -/* Extra definitions that no longer exist in gnutls. - */ -#define GNUTLS_X509_CN_SIZE 256 -#define GNUTLS_X509_C_SIZE 3 -#define GNUTLS_X509_O_SIZE 256 -#define GNUTLS_X509_OU_SIZE 256 -#define GNUTLS_X509_L_SIZE 256 -#define GNUTLS_X509_S_SIZE 256 -#define GNUTLS_X509_EMAIL_SIZE 256 - -typedef struct { - char common_name[GNUTLS_X509_CN_SIZE]; - char country[GNUTLS_X509_C_SIZE]; - char organization[GNUTLS_X509_O_SIZE]; - char organizational_unit_name[GNUTLS_X509_OU_SIZE]; - char locality_name[GNUTLS_X509_L_SIZE]; - char state_or_province_name[GNUTLS_X509_S_SIZE]; - char email[GNUTLS_X509_EMAIL_SIZE]; -} gnutls_x509_dn; - -#define OPENSSL_VERSION_NUMBER (0x0090604F) -#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER -#define OPENSSL_VERSION_TEXT ("GNUTLS " GNUTLS_VERSION " ") - -#define SSL_ERROR_NONE (0) -#define SSL_ERROR_SSL (1) -#define SSL_ERROR_WANT_READ (2) -#define SSL_ERROR_WANT_WRITE (3) -#define SSL_ERROR_SYSCALL (5) -#define SSL_ERROR_ZERO_RETURN (6) - -#define SSL_FILETYPE_PEM (GNUTLS_X509_FMT_PEM) - -#define SSL_VERIFY_NONE (0) - -#define SSL_ST_OK (1) - -#define X509_V_ERR_CERT_NOT_YET_VALID (1) -#define X509_V_ERR_CERT_HAS_EXPIRED (2) -#define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (3) - -#define SSL_OP_ALL (0x000FFFFF) -#define SSL_OP_NO_TLSv1 (0x0400000) - -#define SSL_MODE_ENABLE_PARTIAL_WRITE (0x1) -#define SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER (0x2) -#define SSL_MODE_AUTO_RETRY (0x4) - -#undef X509_NAME -#undef X509 -typedef gnutls_x509_dn X509_NAME; -typedef gnutls_datum_t X509; - -typedef struct _SSL SSL; - -typedef struct { - char priority_string[256]; - unsigned int connend; -} SSL_METHOD; - -typedef struct { - gnutls_protocol_t version; - gnutls_cipher_algorithm_t cipher; - gnutls_kx_algorithm_t kx; - gnutls_mac_algorithm_t mac; - gnutls_compression_method_t compression; - gnutls_certificate_type_t cert; -} SSL_CIPHER; - -typedef struct _BIO { - gnutls_transport_ptr_t fd; -} BIO; - -typedef struct { - SSL *ssl; - int error; - const gnutls_datum_t *cert_list; -#define current_cert cert_list -} X509_STORE_CTX; - -#define X509_STORE_CTX_get_current_cert(ctx) ((ctx)->current_cert) - -typedef struct _SSL_CTX { - SSL_METHOD *method; - char *certfile; - int certfile_type; - char *keyfile; - int keyfile_type; - unsigned long options; - - int (*verify_callback)(int, X509_STORE_CTX *); - int verify_mode; - -} SSL_CTX; - -struct _SSL { - gnutls_session_t gnutls_state; - - gnutls_certificate_client_credentials gnutls_cred; - - SSL_CTX *ctx; - SSL_CIPHER ciphersuite; - - int last_error; - int shutdown; - int state; - unsigned long options; - - int (*verify_callback)(int, X509_STORE_CTX *); - int verify_mode; - - gnutls_transport_ptr_t rfd; - gnutls_transport_ptr_t wfd; -}; - -#define rbio gnutls_state - -typedef struct { - void *handle; -} MD_CTX; - -struct rsa_st; -typedef struct rsa_st RSA; - -#define MD5_CTX MD_CTX -#define RIPEMD160_CTX MD_CTX - -#define OpenSSL_add_ssl_algorithms() SSL_library_init() -#define SSLeay_add_ssl_algorithms() SSL_library_init() -#define SSLeay_add_all_algorithms() OpenSSL_add_all_algorithms() - -#define SSL_get_cipher_name(ssl) \ - SSL_CIPHER_get_name(SSL_get_current_cipher(ssl)) -#define SSL_get_cipher(ssl) SSL_get_cipher_name(ssl) -#define SSL_get_cipher_bits(ssl, bp) \ - SSL_CIPHER_get_bits(SSL_get_current_cipher(ssl), (bp)) -#define SSL_get_cipher_version(ssl) \ - SSL_CIPHER_get_version(SSL_get_current_cipher(ssl)) - -/* Library initialisation functions */ - -int SSL_library_init(void); -void OpenSSL_add_all_algorithms(void); - -/* SSL_CTX structure handling */ - -SSL_CTX *SSL_CTX_new(SSL_METHOD *method); -void SSL_CTX_free(SSL_CTX *ctx); -int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx); -int SSL_CTX_use_certificate_file(SSL_CTX *ctx, const char *certfile, int type); -int SSL_CTX_use_PrivateKey_file(SSL_CTX *ctx, const char *keyfile, int type); -void SSL_CTX_set_verify(SSL_CTX *ctx, int verify_mode, - int (*verify_callback)(int, X509_STORE_CTX *)); -unsigned long SSL_CTX_set_options(SSL_CTX *ctx, unsigned long options); -long SSL_CTX_set_mode(SSL_CTX *ctx, long mode); -int SSL_CTX_set_cipher_list(SSL_CTX *ctx, const char *list); - -/* SSL_CTX statistics */ - -long SSL_CTX_sess_number(SSL_CTX *ctx); -long SSL_CTX_sess_connect(SSL_CTX *ctx); -long SSL_CTX_sess_connect_good(SSL_CTX *ctx); -long SSL_CTX_sess_connect_renegotiate(SSL_CTX *ctx); -long SSL_CTX_sess_accept(SSL_CTX *ctx); -long SSL_CTX_sess_accept_good(SSL_CTX *ctx); -long SSL_CTX_sess_accept_renegotiate(SSL_CTX *ctx); -long SSL_CTX_sess_hits(SSL_CTX *ctx); -long SSL_CTX_sess_misses(SSL_CTX *ctx); -long SSL_CTX_sess_timeouts(SSL_CTX *ctx); - -/* SSL structure handling */ - -SSL *SSL_new(SSL_CTX *ctx); -void SSL_free(SSL *ssl); -void SSL_load_error_strings(void); -int SSL_get_error(SSL *ssl, int ret); -int SSL_set_fd(SSL *ssl, int fd); -int SSL_set_rfd(SSL *ssl, int fd); -int SSL_set_wfd(SSL *ssl, int fd); -void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio); -void SSL_set_connect_state(SSL *ssl); -int SSL_pending(SSL *ssl); -void SSL_set_verify(SSL *ssl, int verify_mode, - int (*verify_callback)(int, X509_STORE_CTX *)); -const X509 *SSL_get_peer_certificate(SSL *ssl); - -/* SSL connection open/close/read/write functions */ - -int SSL_connect(SSL *ssl); -int SSL_accept(SSL *ssl); -int SSL_shutdown(SSL *ssl); -int SSL_read(SSL *ssl, void *buf, int len); -int SSL_write(SSL *ssl, const void *buf, int len); - -int SSL_want(SSL *ssl); - -#define SSL_NOTHING (1) -#define SSL_WRITING (2) -#define SSL_READING (3) -#define SSL_X509_LOOKUP (4) - -#define SSL_want_nothing(s) (SSL_want(s) == SSL_NOTHING) -#define SSL_want_read(s) (SSL_want(s) == SSL_READING) -#define SSL_want_write(s) (SSL_want(s) == SSL_WRITING) -#define SSL_want_x509_lookup(s) (SSL_want(s) == SSL_X509_LOOKUP) - -/* SSL_METHOD functions */ - -SSL_METHOD *SSLv23_client_method(void); -SSL_METHOD *SSLv23_server_method(void); -SSL_METHOD *SSLv3_client_method(void); -SSL_METHOD *SSLv3_server_method(void); -SSL_METHOD *TLSv1_client_method(void); -SSL_METHOD *TLSv1_server_method(void); - -/* SSL_CIPHER functions */ - -SSL_CIPHER *SSL_get_current_cipher(SSL *ssl); -const char *SSL_CIPHER_get_name(SSL_CIPHER *cipher); -int SSL_CIPHER_get_bits(SSL_CIPHER *cipher, int *bits); -const char *SSL_CIPHER_get_version(SSL_CIPHER *cipher); -char *SSL_CIPHER_description(SSL_CIPHER *cipher, char *buf, int size); - -/* X509 functions */ - -X509_NAME *X509_get_subject_name(const X509 *cert); -X509_NAME *X509_get_issuer_name(const X509 *cert); -char *X509_NAME_oneline(gnutls_x509_dn *name, char *buf, int len); -void X509_free(const X509 *cert); - -/* BIO functions */ - -void BIO_get_fd(gnutls_session_t gnutls_state, int *fd); -BIO *BIO_new_socket(int sock, int close_flag); - -/* error handling */ - -unsigned long ERR_get_error(void); -const char *ERR_error_string(unsigned long e, char *buf); - -/* RAND functions */ - -int RAND_status(void); -void RAND_seed(const void *buf, int num); -int RAND_bytes(unsigned char *buf, int num); -int RAND_pseudo_bytes(unsigned char *buf, int num); -const char *RAND_file_name(char *buf, size_t len); -int RAND_load_file(const char *name, long maxbytes); -int RAND_write_file(const char *name); - -int RAND_egd_bytes(const char *path, int bytes); -#define RAND_egd(p) RAND_egd_bytes((p), 255) - -/* message digest functions */ - -#define MD5_DIGEST_LENGTH 16 - -void MD5_Init(MD5_CTX *ctx); -void MD5_Update(MD5_CTX *ctx, const void *buf, int len); -void MD5_Final(unsigned char *md, MD5_CTX *ctx); -unsigned char *MD5(const unsigned char *buf, unsigned long len, - unsigned char *md); - -void RIPEMD160_Init(RIPEMD160_CTX *ctx); -void RIPEMD160_Update(RIPEMD160_CTX *ctx, const void *buf, int len); -void RIPEMD160_Final(unsigned char *md, RIPEMD160_CTX *ctx); -unsigned char *RIPEMD160(const unsigned char *buf, unsigned long len, - unsigned char *md); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/x509.h b/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/x509.h index 4466155..c89255c 100644 --- a/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/x509.h +++ b/V2G_Libraries/Third_Party/GnuTLS/include/gnutls/x509.h @@ -410,6 +410,14 @@ void gnutls_x509_spki_set_rsa_pss_params(gnutls_x509_spki_t spki, gnutls_digest_algorithm_t dig, unsigned int salt_size); +int gnutls_x509_spki_set_rsa_oaep_params(gnutls_x509_spki_t spki, + gnutls_digest_algorithm_t dig, + const gnutls_datum_t *label); + +int gnutls_x509_spki_get_rsa_oaep_params(gnutls_x509_spki_t spki, + gnutls_digest_algorithm_t *dig, + gnutls_datum_t *label); + int gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert, unsigned int *bits); int gnutls_x509_crt_set_spki(gnutls_x509_crt_t crt, From 154657bef681fdae78196750178005bcb5f1a6dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoren=20Gr=C3=BCttemeier?= Date: Fri, 20 Sep 2024 13:18:18 +0200 Subject: [PATCH 4/7] fixed X509SerialNumber --- V2G_Libraries/Third_Party/cbv2g/din/din_msgDefDecoder.c | 4 ++-- V2G_Libraries/Third_Party/cbv2g/iso-2/iso2_msgDefDecoder.c | 4 ++-- V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_ACDP_Decoder.c | 4 ++-- V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_AC_Decoder.c | 4 ++-- .../Third_Party/cbv2g/iso-20/iso20_CommonMessages_Decoder.c | 4 ++-- V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_DC_Decoder.c | 4 ++-- V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_WPT_Decoder.c | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/V2G_Libraries/Third_Party/cbv2g/din/din_msgDefDecoder.c b/V2G_Libraries/Third_Party/cbv2g/din/din_msgDefDecoder.c index 01dc519..a445b1c 100644 --- a/V2G_Libraries/Third_Party/cbv2g/din/din_msgDefDecoder.c +++ b/V2G_Libraries/Third_Party/cbv2g/din/din_msgDefDecoder.c @@ -2260,7 +2260,7 @@ static int decode_din_X509IssuerSerialType(exi_bitstream_t* stream, struct din_X uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(X509IssuerSerialType->X509SerialNumber.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; @@ -5268,7 +5268,7 @@ static int decode_din_SignatureMethodType(exi_bitstream_t* stream, struct din_Si uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(SignatureMethodType->HMACOutputLength.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; diff --git a/V2G_Libraries/Third_Party/cbv2g/iso-2/iso2_msgDefDecoder.c b/V2G_Libraries/Third_Party/cbv2g/iso-2/iso2_msgDefDecoder.c index fdc9b8b..c647feb 100644 --- a/V2G_Libraries/Third_Party/cbv2g/iso-2/iso2_msgDefDecoder.c +++ b/V2G_Libraries/Third_Party/cbv2g/iso-2/iso2_msgDefDecoder.c @@ -1549,7 +1549,7 @@ static int decode_iso2_X509IssuerSerialType(exi_bitstream_t* stream, struct iso2 uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(X509IssuerSerialType->X509SerialNumber.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; @@ -2336,7 +2336,7 @@ static int decode_iso2_SignatureMethodType(exi_bitstream_t* stream, struct iso2_ uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(SignatureMethodType->HMACOutputLength.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; diff --git a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_ACDP_Decoder.c b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_ACDP_Decoder.c index 6fe6993..c6b69fa 100644 --- a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_ACDP_Decoder.c +++ b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_ACDP_Decoder.c @@ -1230,7 +1230,7 @@ static int decode_iso20_acdp_X509IssuerSerialType(exi_bitstream_t* stream, struc uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(X509IssuerSerialType->X509SerialNumber.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; @@ -1885,7 +1885,7 @@ static int decode_iso20_acdp_SignatureMethodType(exi_bitstream_t* stream, struct uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(SignatureMethodType->HMACOutputLength.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; diff --git a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_AC_Decoder.c b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_AC_Decoder.c index 73eec0f..ff8118f 100644 --- a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_AC_Decoder.c +++ b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_AC_Decoder.c @@ -1246,7 +1246,7 @@ static int decode_iso20_ac_X509IssuerSerialType(exi_bitstream_t* stream, struct uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(X509IssuerSerialType->X509SerialNumber.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; @@ -1901,7 +1901,7 @@ static int decode_iso20_ac_SignatureMethodType(exi_bitstream_t* stream, struct i uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(SignatureMethodType->HMACOutputLength.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; diff --git a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_CommonMessages_Decoder.c b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_CommonMessages_Decoder.c index eb6e8aa..b1dae09 100644 --- a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_CommonMessages_Decoder.c +++ b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_CommonMessages_Decoder.c @@ -1312,7 +1312,7 @@ static int decode_iso20_X509IssuerSerialType(exi_bitstream_t* stream, struct iso uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(X509IssuerSerialType->X509SerialNumber.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; @@ -2122,7 +2122,7 @@ static int decode_iso20_SignatureMethodType(exi_bitstream_t* stream, struct iso2 uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(SignatureMethodType->HMACOutputLength.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; diff --git a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_DC_Decoder.c b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_DC_Decoder.c index 89569e1..fe9db5d 100644 --- a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_DC_Decoder.c +++ b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_DC_Decoder.c @@ -1252,7 +1252,7 @@ static int decode_iso20_dc_X509IssuerSerialType(exi_bitstream_t* stream, struct uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(X509IssuerSerialType->X509SerialNumber.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; @@ -1907,7 +1907,7 @@ static int decode_iso20_dc_SignatureMethodType(exi_bitstream_t* stream, struct i uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(SignatureMethodType->HMACOutputLength.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; diff --git a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_WPT_Decoder.c b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_WPT_Decoder.c index 78508c3..ffae92d 100644 --- a/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_WPT_Decoder.c +++ b/V2G_Libraries/Third_Party/cbv2g/iso-20/iso20_WPT_Decoder.c @@ -1263,7 +1263,7 @@ static int decode_iso20_wpt_X509IssuerSerialType(exi_bitstream_t* stream, struct uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(X509IssuerSerialType->X509SerialNumber.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; @@ -2042,7 +2042,7 @@ static int decode_iso20_wpt_SignatureMethodType(exi_bitstream_t* stream, struct uint8_t realInteger[21] = {0}; // max length: 20 + 1 zero terminator size_t realInteger_size = 0; int result = exi_basetypes_convert_bytes_from_unsigned(&(SignatureMethodType->HMACOutputLength.data), &realInteger[0], &realInteger_size, 21); - if (result != 0) + if (result == 0) { // reverse array uint8_t temp; From 26aeb044171769561cf3576e71b3b0d85fc7fb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoren=20Gr=C3=BCttemeier?= Date: Fri, 20 Sep 2024 13:22:16 +0200 Subject: [PATCH 5/7] v1.4.3 --- Installer/InstallerScript.iss | 2 +- V2G_Libraries/v2gLib/main.rc | 8 ++++---- Wireshark/plugins/v2gcommon.lua | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Installer/InstallerScript.iss b/Installer/InstallerScript.iss index 3ff0441..757e508 100644 --- a/Installer/InstallerScript.iss +++ b/Installer/InstallerScript.iss @@ -1,5 +1,5 @@ // DO NOT CHANGE VERSION HERE! Run update_version.bat -#define AppVer "1.4.2" +#define AppVer "1.4.3" #define AppId "dsV2Gshark" [Setup] diff --git a/V2G_Libraries/v2gLib/main.rc b/V2G_Libraries/v2gLib/main.rc index a124ccf..363829d 100644 --- a/V2G_Libraries/v2gLib/main.rc +++ b/V2G_Libraries/v2gLib/main.rc @@ -1,11 +1,11 @@ #include -#define VER_FILEVERSION 1,4,2,0 -#define VER_FILEVERSION_STR "1.4.2.0\0" +#define VER_FILEVERSION 1,4,3,0 +#define VER_FILEVERSION_STR "1.4.3.0\0" #define VER_COMPANYNAME_STR "dSPACE GmbH" #define VER_PRODUCTNAME_STR "V2gLib" -#define VER_PRODUCTVERSION 1,4,2,0 -#define VER_PRODUCTVERSION_STR "1.4.2.0\0" +#define VER_PRODUCTVERSION 1,4,3,0 +#define VER_PRODUCTVERSION_STR "1.4.3.0\0" VS_VERSION_INFO VERSIONINFO FILEVERSION VER_FILEVERSION diff --git a/Wireshark/plugins/v2gcommon.lua b/Wireshark/plugins/v2gcommon.lua index 6ff82a1..a6639c8 100644 --- a/Wireshark/plugins/v2gcommon.lua +++ b/Wireshark/plugins/v2gcommon.lua @@ -63,7 +63,7 @@ function v2gcommon.load_v2gLib() end end -v2gcommon.DS_V2GSHARK_VERSION = "1.4.2" -- DO NOT CHANGE +v2gcommon.DS_V2GSHARK_VERSION = "1.4.3" -- DO NOT CHANGE -- extend path (where to load .lua files) if not string.find(v2gcommon.get_plugins_file_path(), package.path) then From 8842b21952bf1c0471fd39428ad26070a30d9963 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoren=20Gr=C3=BCttemeier?= Date: Wed, 25 Sep 2024 14:15:25 +0200 Subject: [PATCH 6/7] updated README --- Images/WS_ISO15118_2_Cert_Error.png | Bin 0 -> 50344 bytes README.md | 7 ++++++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 Images/WS_ISO15118_2_Cert_Error.png diff --git a/Images/WS_ISO15118_2_Cert_Error.png b/Images/WS_ISO15118_2_Cert_Error.png new file mode 100644 index 0000000000000000000000000000000000000000..a4049e6296aeb24bd5e41e24ebbcbc450a98376b GIT binary patch literal 50344 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з#HNSFS!FWCiBw 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 secret disclosure) - LLC diagnostics via HomePlug AV packets for sniffer and debug packets - Automatic schema detection - Detect schema automatically in case of missing SDP or SAP @@ -31,6 +31,9 @@ This Wireshark plugin allows to analyze and decode packets between electric vehi - Wireshark I/O Graph support for V2G packets ### Live TLS Decryption +> To decrypt a TLS session, you need a compatible device (like the dSPACE DS5366) that is capable of disclosing the TLS secret during or after the handshake. Please note that this feature is not available on productive devices. It is not possible to decrypt regular TLS sessions. +> If a session with a disclosed TLS secret was recorded without dsV2Gshark being installed, you can still decrypt the data by installing dsV2Gshark afterwards. + The plugin processes a TLS master secret disclosure packet after handshake to decode the following V2G session. The disclosure message is a UDP packet within the source port range 49152-65535 (see Wireshark protocol settings) containing the ASCII string `CLIENT_RANDOM <32-byte client random> <48-byte master secret>` as payload data (TLS 1.2). This disclosure message has to be sent from one of the communication partners in a testing environment. For TLS 1.3 decryption you have to provide different secrets: `CLIENT_HANDSHAKE_TRAFFIC_SECRET`, `SERVER_HANDSHAKE_TRAFFIC_SECRET`, `EXPORTER_SECRET`, `CLIENT_TRAFFIC_SECRET_`, `SERVER_TRAFFIC_SECRET_`. You can send one UDP packet for each secret or combine the secrets in one UDP packet (separated by line breaks). @@ -86,6 +89,8 @@ Click on a packet in the graph to inspect it in the Wireshark main window. Press ![ISO 15118-2 Certificates](Images/WS_ISO15118_2_Certificate.png) ### Message Validation ![ISO 15118-20 Message Validation](Images/WS_ISO15118_20_MsgValidation.png) +### Certificate Check +![ISO 15118-2 Certificate Check](Images/WS_ISO15118_2_Cert_Error.png) ### Live TLS Decryption ![ISO 15118-2 Live TLS](Images/WS_ISO15118_2_LiveTLS.png) ### Filter Buttons From 296e90e66b57be606ebfbd0ab4f3fced6f88fe81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thoren=20Gr=C3=BCttemeier?= <156680693+TGruett@users.noreply.github.com> Date: Wed, 25 Sep 2024 14:33:06 +0200 Subject: [PATCH 7/7] use alerts in README --- README.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index df3f429..27403aa 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,8 @@ [![Release](https://img.shields.io/github/v/release/dspace-group/dsV2Gshark?label=release)](https://github.com/dspace-group/dsV2Gshark/releases) [![dSPACE](https://img.shields.io/badge/-OpenSource%20powered%20by%20dSPACE-blue)](https://www.dspace.com/) -## ❗Wireshark 4.4+ requires dsV2Gshark 1.4.2 or higher❗ +> [!IMPORTANT] +**Wireshark 4.4+ requires dsV2Gshark 1.4.2 or higher** ## Overview This Wireshark plugin allows to analyze and decode packets between electric vehicles (EV) and charging stations (EVSE), also known as V2G messages. @@ -31,12 +32,14 @@ This Wireshark plugin allows to analyze and decode packets between electric vehi - Wireshark I/O Graph support for V2G packets ### Live TLS Decryption -> To decrypt a TLS session, you need a compatible device (like the dSPACE DS5366) that is capable of disclosing the TLS secret during or after the handshake. Please note that this feature is not available on productive devices. It is not possible to decrypt regular TLS sessions. -> If a session with a disclosed TLS secret was recorded without dsV2Gshark being installed, you can still decrypt the data by installing dsV2Gshark afterwards. +> [!IMPORTANT] +To decrypt a TLS session, you need a compatible device (like the dSPACE DS5366) that is capable of disclosing the TLS secret during or after the handshake. Please note that this feature is not available on productive devices. It is not possible to decrypt regular TLS sessions. +If a session with a disclosed TLS secret was recorded without dsV2Gshark being installed, you can still decrypt the data by installing dsV2Gshark afterwards. The plugin processes a TLS master secret disclosure packet after handshake to decode the following V2G session. The disclosure message is a UDP packet within the source port range 49152-65535 (see Wireshark protocol settings) containing the ASCII string `CLIENT_RANDOM <32-byte client random> <48-byte master secret>` as payload data (TLS 1.2). This disclosure message has to be sent from one of the communication partners in a testing environment. For TLS 1.3 decryption you have to provide different secrets: `CLIENT_HANDSHAKE_TRAFFIC_SECRET`, `SERVER_HANDSHAKE_TRAFFIC_SECRET`, `EXPORTER_SECRET`, `CLIENT_TRAFFIC_SECRET_`, `SERVER_TRAFFIC_SECRET_`. You can send one UDP packet for each secret or combine the secrets in one UDP packet (separated by line breaks). +> [!NOTE] In order to autodecrypt charging sessions using non-GUI versions of Wireshark (e.g. tshark), the full PCAP must be parsed twice. This is because there is no matching redissection trigger available in the Wireshark API. ### Wireshark I/O Graph